diff options
Diffstat (limited to 'compiler/rustc_session/src/config.rs')
-rw-r--r-- | compiler/rustc_session/src/config.rs | 2970 |
1 files changed, 2970 insertions, 0 deletions
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs new file mode 100644 index 000000000..6a8298605 --- /dev/null +++ b/compiler/rustc_session/src/config.rs @@ -0,0 +1,2970 @@ +//! Contains infrastructure for configuring the compiler, including parsing +//! command-line options. + +pub use crate::options::*; + +use crate::search_paths::SearchPath; +use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; +use crate::{early_error, early_warn, Session}; +use crate::{lint, HashStableContext}; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; + +use rustc_data_structures::stable_hasher::ToStableHashKey; +use rustc_target::abi::{Align, TargetDataLayout}; +use rustc_target::spec::{LinkerFlavor, SplitDebuginfo, Target, TargetTriple, TargetWarnings}; +use rustc_target::spec::{PanicStrategy, SanitizerSet, TARGETS}; + +use crate::parse::{CrateCheckConfig, CrateConfig}; +use rustc_feature::UnstableFeatures; +use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION}; +use rustc_span::source_map::{FileName, FilePathMapping}; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::RealFileName; +use rustc_span::SourceFileHashAlgorithm; + +use rustc_errors::emitter::HumanReadableErrorType; +use rustc_errors::{ColorConfig, HandlerFlags}; + +use std::collections::btree_map::{ + Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, +}; +use std::collections::{BTreeMap, BTreeSet}; +use std::fmt; +use std::hash::Hash; +use std::iter::{self, FromIterator}; +use std::path::{Path, PathBuf}; +use std::str::{self, FromStr}; + +/// The different settings that the `-C strip` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum Strip { + /// Do not strip at all. + None, + + /// Strip debuginfo. + Debuginfo, + + /// Strip all symbols. + Symbols, +} + +/// The different settings that the `-C control-flow-guard` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CFGuard { + /// Do not emit Control Flow Guard metadata or checks. + Disabled, + + /// Emit Control Flow Guard metadata but no checks. + NoChecks, + + /// Emit Control Flow Guard metadata and checks. + Checks, +} + +/// The different settings that the `-Z cf-protection` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CFProtection { + /// Do not enable control-flow protection + None, + + /// Emit control-flow protection for branches (enables indirect branch tracking). + Branch, + + /// Emit control-flow protection for returns. + Return, + + /// Emit control-flow protection for both branches and returns. + Full, +} + +#[derive(Clone, Copy, Debug, PartialEq, Hash, HashStable_Generic)] +pub enum OptLevel { + No, // -O0 + Less, // -O1 + Default, // -O2 + Aggressive, // -O3 + Size, // -Os + SizeMin, // -Oz +} + +/// This is what the `LtoCli` values get mapped to after resolving defaults and +/// and taking other command line options into account. +/// +/// Note that linker plugin-based LTO is a different mechanism entirely. +#[derive(Clone, PartialEq)] +pub enum Lto { + /// Don't do any LTO whatsoever. + No, + + /// Do a full-crate-graph (inter-crate) LTO with ThinLTO. + Thin, + + /// Do a local ThinLTO (intra-crate, over the CodeGen Units of the local crate only). This is + /// only relevant if multiple CGUs are used. + ThinLocal, + + /// Do a full-crate-graph (inter-crate) LTO with "fat" LTO. + Fat, +} + +/// The different settings that the `-C lto` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum LtoCli { + /// `-C lto=no` + No, + /// `-C lto=yes` + Yes, + /// `-C lto` + NoParam, + /// `-C lto=thin` + Thin, + /// `-C lto=fat` + Fat, + /// No `-C lto` flag passed + Unspecified, +} + +/// The different settings that the `-Z dump_mir_spanview` flag can have. `Statement` generates a +/// document highlighting each span of every statement (including terminators). `Terminator` and +/// `Block` highlight a single span per `BasicBlock`: the span of the block's `Terminator`, or a +/// computed span for the block, representing the entire range, covering the block's terminator and +/// all of its statements. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum MirSpanview { + /// Default `-Z dump_mir_spanview` or `-Z dump_mir_spanview=statement` + Statement, + /// `-Z dump_mir_spanview=terminator` + Terminator, + /// `-Z dump_mir_spanview=block` + Block, +} + +/// The different settings that the `-C instrument-coverage` flag can have. +/// +/// Coverage instrumentation now supports combining `-C instrument-coverage` +/// with compiler and linker optimization (enabled with `-O` or `-C opt-level=1` +/// and higher). Nevertheless, there are many variables, depending on options +/// selected, code structure, and enabled attributes. If errors are encountered, +/// either while compiling or when generating `llvm-cov show` reports, consider +/// lowering the optimization level, including or excluding `-C link-dead-code`, +/// or using `-Zunstable-options -C instrument-coverage=except-unused-functions` +/// or `-Zunstable-options -C instrument-coverage=except-unused-generics`. +/// +/// Note that `ExceptUnusedFunctions` means: When `mapgen.rs` generates the +/// coverage map, it will not attempt to generate synthetic functions for unused +/// (and not code-generated) functions (whether they are generic or not). As a +/// result, non-codegenned functions will not be included in the coverage map, +/// and will not appear, as covered or uncovered, in coverage reports. +/// +/// `ExceptUnusedGenerics` will add synthetic functions to the coverage map, +/// unless the function has type parameters. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum InstrumentCoverage { + /// Default `-C instrument-coverage` or `-C instrument-coverage=statement` + All, + /// `-Zunstable-options -C instrument-coverage=except-unused-generics` + ExceptUnusedGenerics, + /// `-Zunstable-options -C instrument-coverage=except-unused-functions` + ExceptUnusedFunctions, + /// `-C instrument-coverage=off` (or `no`, etc.) + Off, +} + +#[derive(Clone, PartialEq, Hash, Debug)] +pub enum LinkerPluginLto { + LinkerPlugin(PathBuf), + LinkerPluginAuto, + Disabled, +} + +/// Used with `-Z assert-incr-state`. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum IncrementalStateAssertion { + /// Found and loaded an existing session directory. + /// + /// Note that this says nothing about whether any particular query + /// will be found to be red or green. + Loaded, + /// Did not load an existing session directory. + NotLoaded, +} + +impl LinkerPluginLto { + pub fn enabled(&self) -> bool { + match *self { + LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true, + LinkerPluginLto::Disabled => false, + } + } +} + +/// The different settings that can be enabled via the `-Z location-detail` flag. +#[derive(Clone, PartialEq, Hash, Debug)] +pub struct LocationDetail { + pub file: bool, + pub line: bool, + pub column: bool, +} + +impl LocationDetail { + pub fn all() -> Self { + Self { file: true, line: true, column: true } + } +} + +#[derive(Clone, PartialEq, Hash, Debug)] +pub enum SwitchWithOptPath { + Enabled(Option<PathBuf>), + Disabled, +} + +impl SwitchWithOptPath { + pub fn enabled(&self) -> bool { + match *self { + SwitchWithOptPath::Enabled(_) => true, + SwitchWithOptPath::Disabled => false, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable_Generic)] +#[derive(Encodable, Decodable)] +pub enum SymbolManglingVersion { + Legacy, + V0, +} + +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum DebugInfo { + None, + Limited, + Full, +} + +/// Split debug-information is enabled by `-C split-debuginfo`, this enum is only used if split +/// debug-information is enabled (in either `Packed` or `Unpacked` modes), and the platform +/// uses DWARF for debug-information. +/// +/// Some debug-information requires link-time relocation and some does not. LLVM can partition +/// the debuginfo into sections depending on whether or not it requires link-time relocation. Split +/// DWARF provides a mechanism which allows the linker to skip the sections which don't require +/// link-time relocation - either by putting those sections in DWARF object files, or by keeping +/// them in the object file in such a way that the linker will skip them. +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum SplitDwarfKind { + /// Sections which do not require relocation are written into object file but ignored by the + /// linker. + Single, + /// Sections which do not require relocation are written into a DWARF object (`.dwo`) file + /// which is ignored by the linker. + Split, +} + +impl FromStr for SplitDwarfKind { + type Err = (); + + fn from_str(s: &str) -> Result<Self, ()> { + Ok(match s { + "single" => SplitDwarfKind::Single, + "split" => SplitDwarfKind::Split, + _ => return Err(()), + }) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)] +#[derive(Encodable, Decodable)] +pub enum OutputType { + Bitcode, + Assembly, + LlvmAssembly, + Mir, + Metadata, + Object, + Exe, + DepInfo, +} + +impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType { + type KeyType = Self; + + fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { + *self + } +} + +impl OutputType { + fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool { + match *self { + OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true, + OutputType::Bitcode + | OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::Object => false, + } + } + + fn shorthand(&self) -> &'static str { + match *self { + OutputType::Bitcode => "llvm-bc", + OutputType::Assembly => "asm", + OutputType::LlvmAssembly => "llvm-ir", + OutputType::Mir => "mir", + OutputType::Object => "obj", + OutputType::Metadata => "metadata", + OutputType::Exe => "link", + OutputType::DepInfo => "dep-info", + } + } + + fn from_shorthand(shorthand: &str) -> Option<Self> { + Some(match shorthand { + "asm" => OutputType::Assembly, + "llvm-ir" => OutputType::LlvmAssembly, + "mir" => OutputType::Mir, + "llvm-bc" => OutputType::Bitcode, + "obj" => OutputType::Object, + "metadata" => OutputType::Metadata, + "link" => OutputType::Exe, + "dep-info" => OutputType::DepInfo, + _ => return None, + }) + } + + fn shorthands_display() -> String { + format!( + "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`", + OutputType::Bitcode.shorthand(), + OutputType::Assembly.shorthand(), + OutputType::LlvmAssembly.shorthand(), + OutputType::Mir.shorthand(), + OutputType::Object.shorthand(), + OutputType::Metadata.shorthand(), + OutputType::Exe.shorthand(), + OutputType::DepInfo.shorthand(), + ) + } + + pub fn extension(&self) -> &'static str { + match *self { + OutputType::Bitcode => "bc", + OutputType::Assembly => "s", + OutputType::LlvmAssembly => "ll", + OutputType::Mir => "mir", + OutputType::Object => "o", + OutputType::Metadata => "rmeta", + OutputType::DepInfo => "d", + OutputType::Exe => "", + } + } +} + +/// The type of diagnostics output to generate. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ErrorOutputType { + /// Output meant for the consumption of humans. + HumanReadable(HumanReadableErrorType), + /// Output that's consumed by other tools such as `rustfix` or the `RLS`. + Json { + /// Render the JSON in a human readable way (with indents and newlines). + pretty: bool, + /// The JSON output includes a `rendered` field that includes the rendered + /// human output. + json_rendered: HumanReadableErrorType, + }, +} + +impl Default for ErrorOutputType { + fn default() -> Self { + Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto)) + } +} + +/// Parameter to control path trimming. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] +pub enum TrimmedDefPaths { + /// `try_print_trimmed_def_path` never prints a trimmed path and never calls the expensive query + #[default] + Never, + /// `try_print_trimmed_def_path` calls the expensive query, the query doesn't call `delay_good_path_bug` + Always, + /// `try_print_trimmed_def_path` calls the expensive query, the query calls `delay_good_path_bug` + GoodPath, +} + +/// Use tree-based collections to cheaply get a deterministic `Hash` implementation. +/// *Do not* switch `BTreeMap` out for an unsorted container type! That would break +/// dependency tracking for command-line arguments. Also only hash keys, since tracking +/// should only depend on the output types, not the paths they're written to. +#[derive(Clone, Debug, Hash, HashStable_Generic)] +pub struct OutputTypes(BTreeMap<OutputType, Option<PathBuf>>); + +impl OutputTypes { + pub fn new(entries: &[(OutputType, Option<PathBuf>)]) -> OutputTypes { + OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone())))) + } + + pub fn get(&self, key: &OutputType) -> Option<&Option<PathBuf>> { + self.0.get(key) + } + + pub fn contains_key(&self, key: &OutputType) -> bool { + self.0.contains_key(key) + } + + pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option<PathBuf>> { + self.0.keys() + } + + pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option<PathBuf>> { + self.0.values() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns `true` if any of the output types require codegen or linking. + pub fn should_codegen(&self) -> bool { + self.0.keys().any(|k| match *k { + OutputType::Bitcode + | OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::Object + | OutputType::Exe => true, + OutputType::Metadata | OutputType::DepInfo => false, + }) + } + + /// Returns `true` if any of the output types require linking. + pub fn should_link(&self) -> bool { + self.0.keys().any(|k| match *k { + OutputType::Bitcode + | OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::Metadata + | OutputType::Object + | OutputType::DepInfo => false, + OutputType::Exe => true, + }) + } +} + +/// Use tree-based collections to cheaply get a deterministic `Hash` implementation. +/// *Do not* switch `BTreeMap` or `BTreeSet` out for an unsorted container type! That +/// would break dependency tracking for command-line arguments. +#[derive(Clone)] +pub struct Externs(BTreeMap<String, ExternEntry>); + +#[derive(Clone, Debug)] +pub struct ExternEntry { + pub location: ExternLocation, + /// Indicates this is a "private" dependency for the + /// `exported_private_dependencies` lint. + /// + /// This can be set with the `priv` option like + /// `--extern priv:name=foo.rlib`. + pub is_private_dep: bool, + /// Add the extern entry to the extern prelude. + /// + /// This can be disabled with the `noprelude` option like + /// `--extern noprelude:name`. + pub add_prelude: bool, + /// The extern entry shouldn't be considered for unused dependency warnings. + /// + /// `--extern nounused:std=/path/to/lib/libstd.rlib`. This is used to + /// suppress `unused-crate-dependencies` warnings. + pub nounused_dep: bool, +} + +#[derive(Clone, Debug)] +pub enum ExternLocation { + /// Indicates to look for the library in the search paths. + /// + /// Added via `--extern name`. + FoundInLibrarySearchDirectories, + /// The locations where this extern entry must be found. + /// + /// The `CrateLoader` is responsible for loading these and figuring out + /// which one to use. + /// + /// Added via `--extern prelude_name=some_file.rlib` + ExactPaths(BTreeSet<CanonicalizedPath>), +} + +impl Externs { + /// Used for testing. + pub fn new(data: BTreeMap<String, ExternEntry>) -> Externs { + Externs(data) + } + + pub fn get(&self, key: &str) -> Option<&ExternEntry> { + self.0.get(key) + } + + pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> { + self.0.iter() + } + + pub fn len(&self) -> usize { + self.0.len() + } +} + +impl ExternEntry { + fn new(location: ExternLocation) -> ExternEntry { + ExternEntry { location, is_private_dep: false, add_prelude: false, nounused_dep: false } + } + + pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> { + match &self.location { + ExternLocation::ExactPaths(set) => Some(set.iter()), + _ => None, + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum PrintRequest { + FileNames, + Sysroot, + TargetLibdir, + CrateName, + Cfg, + TargetList, + TargetCPUs, + TargetFeatures, + RelocationModels, + CodeModels, + TlsModels, + TargetSpec, + NativeStaticLibs, + StackProtectorStrategies, + LinkArgs, +} + +pub enum Input { + /// Load source code from a file. + File(PathBuf), + /// Load source code from a string. + Str { + /// A string that is shown in place of a filename. + name: FileName, + /// An anonymous string containing the source code. + input: String, + }, +} + +impl Input { + pub fn filestem(&self) -> &str { + match *self { + Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(), + Input::Str { .. } => "rust_out", + } + } + + pub fn source_name(&self) -> FileName { + match *self { + Input::File(ref ifile) => ifile.clone().into(), + Input::Str { ref name, .. } => name.clone(), + } + } +} + +#[derive(Clone, Hash, Debug, HashStable_Generic)] +pub struct OutputFilenames { + pub out_directory: PathBuf, + filestem: String, + pub single_output_file: Option<PathBuf>, + pub temps_directory: Option<PathBuf>, + pub outputs: OutputTypes, +} + +pub const RLINK_EXT: &str = "rlink"; +pub const RUST_CGU_EXT: &str = "rcgu"; +pub const DWARF_OBJECT_EXT: &str = "dwo"; + +impl OutputFilenames { + pub fn new( + out_directory: PathBuf, + out_filestem: String, + single_output_file: Option<PathBuf>, + temps_directory: Option<PathBuf>, + extra: String, + outputs: OutputTypes, + ) -> Self { + OutputFilenames { + out_directory, + single_output_file, + temps_directory, + outputs, + filestem: format!("{out_filestem}{extra}"), + } + } + + pub fn path(&self, flavor: OutputType) -> PathBuf { + self.outputs + .get(&flavor) + .and_then(|p| p.to_owned()) + .or_else(|| self.single_output_file.clone()) + .unwrap_or_else(|| self.output_path(flavor)) + } + + /// Gets the output path where a compilation artifact of the given type + /// should be placed on disk. + pub fn output_path(&self, flavor: OutputType) -> PathBuf { + let extension = flavor.extension(); + self.with_directory_and_extension(&self.out_directory, &extension) + } + + /// Gets the path where a compilation artifact of the given type for the + /// given codegen unit should be placed on disk. If codegen_unit_name is + /// None, a path distinct from those of any codegen unit will be generated. + pub fn temp_path(&self, flavor: OutputType, codegen_unit_name: Option<&str>) -> PathBuf { + let extension = flavor.extension(); + self.temp_path_ext(extension, codegen_unit_name) + } + + /// Like `temp_path`, but specifically for dwarf objects. + pub fn temp_path_dwo(&self, codegen_unit_name: Option<&str>) -> PathBuf { + self.temp_path_ext(DWARF_OBJECT_EXT, codegen_unit_name) + } + + /// Like `temp_path`, but also supports things where there is no corresponding + /// OutputType, like noopt-bitcode or lto-bitcode. + pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf { + let mut extension = String::new(); + + if let Some(codegen_unit_name) = codegen_unit_name { + extension.push_str(codegen_unit_name); + } + + if !ext.is_empty() { + if !extension.is_empty() { + extension.push('.'); + extension.push_str(RUST_CGU_EXT); + extension.push('.'); + } + + extension.push_str(ext); + } + + let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory); + + self.with_directory_and_extension(&temps_directory, &extension) + } + + pub fn with_extension(&self, extension: &str) -> PathBuf { + self.with_directory_and_extension(&self.out_directory, extension) + } + + fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf { + let mut path = directory.join(&self.filestem); + path.set_extension(extension); + path + } + + /// Returns the path for the Split DWARF file - this can differ depending on which Split DWARF + /// mode is being used, which is the logic that this function is intended to encapsulate. + pub fn split_dwarf_path( + &self, + split_debuginfo_kind: SplitDebuginfo, + split_dwarf_kind: SplitDwarfKind, + cgu_name: Option<&str>, + ) -> Option<PathBuf> { + let obj_out = self.temp_path(OutputType::Object, cgu_name); + let dwo_out = self.temp_path_dwo(cgu_name); + match (split_debuginfo_kind, split_dwarf_kind) { + (SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None, + // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes + // (pointing at the path which is being determined here). Use the path to the current + // object file. + (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => { + Some(obj_out) + } + // Split mode emits the DWARF into a different file, use that path. + (SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => { + Some(dwo_out) + } + } + } +} + +pub fn host_triple() -> &'static str { + // Get the host triple out of the build environment. This ensures that our + // idea of the host triple is the same as for the set of libraries we've + // actually built. We can't just take LLVM's host triple because they + // normalize all ix86 architectures to i386. + // + // Instead of grabbing the host triple (for the current host), we grab (at + // compile time) the target triple that this rustc is built with and + // calling that (at runtime) the host triple. + (option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE") +} + +impl Default for Options { + fn default() -> Options { + Options { + assert_incr_state: None, + crate_types: Vec::new(), + optimize: OptLevel::No, + debuginfo: DebugInfo::None, + lint_opts: Vec::new(), + lint_cap: None, + describe_lints: false, + output_types: OutputTypes(BTreeMap::new()), + search_paths: vec![], + maybe_sysroot: None, + target_triple: TargetTriple::from_triple(host_triple()), + test: false, + incremental: None, + unstable_opts: Default::default(), + prints: Vec::new(), + cg: Default::default(), + error_format: ErrorOutputType::default(), + diagnostic_width: None, + externs: Externs(BTreeMap::new()), + crate_name: None, + libs: Vec::new(), + unstable_features: UnstableFeatures::Disallow, + debug_assertions: true, + actually_rustdoc: false, + trimmed_def_paths: TrimmedDefPaths::default(), + cli_forced_codegen_units: None, + cli_forced_thinlto_off: false, + remap_path_prefix: Vec::new(), + real_rust_source_base_dir: None, + edition: DEFAULT_EDITION, + json_artifact_notifications: false, + json_unused_externs: JsonUnusedExterns::No, + json_future_incompat: false, + pretty: None, + working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()), + } + } +} + +impl Options { + /// Returns `true` if there is a reason to build the dep graph. + pub fn build_dep_graph(&self) -> bool { + self.incremental.is_some() + || self.unstable_opts.dump_dep_graph + || self.unstable_opts.query_dep_graph + } + + pub fn file_path_mapping(&self) -> FilePathMapping { + FilePathMapping::new(self.remap_path_prefix.clone()) + } + + /// Returns `true` if there will be an output file generated. + pub fn will_create_output_file(&self) -> bool { + !self.unstable_opts.parse_only && // The file is just being parsed + !self.unstable_opts.ls // The file is just being queried + } + + #[inline] + pub fn share_generics(&self) -> bool { + match self.unstable_opts.share_generics { + Some(setting) => setting, + None => match self.optimize { + OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true, + OptLevel::Default | OptLevel::Aggressive => false, + }, + } + } + + pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion { + self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy) + } +} + +impl UnstableOptions { + pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags { + HandlerFlags { + can_emit_warnings, + treat_err_as_bug: self.treat_err_as_bug, + dont_buffer_diagnostics: self.dont_buffer_diagnostics, + report_delayed_bugs: self.report_delayed_bugs, + macro_backtrace: self.macro_backtrace, + deduplicate_diagnostics: self.deduplicate_diagnostics, + } + } +} + +// The type of entry function, so users can have their own entry functions +#[derive(Copy, Clone, PartialEq, Hash, Debug, HashStable_Generic)] +pub enum EntryFnType { + Main, + Start, +} + +#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] +#[derive(HashStable_Generic)] +pub enum CrateType { + Executable, + Dylib, + Rlib, + Staticlib, + Cdylib, + ProcMacro, +} + +impl CrateType { + /// When generated, is this crate type an archive? + pub fn is_archive(&self) -> bool { + match *self { + CrateType::Rlib | CrateType::Staticlib => true, + CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => { + false + } + } + } +} + +#[derive(Clone, Hash, Debug, PartialEq, Eq)] +pub enum Passes { + Some(Vec<String>), + All, +} + +impl Passes { + pub fn is_empty(&self) -> bool { + match *self { + Passes::Some(ref v) => v.is_empty(), + Passes::All => false, + } + } + + pub fn extend(&mut self, passes: impl IntoIterator<Item = String>) { + match *self { + Passes::Some(ref mut v) => v.extend(passes), + Passes::All => {} + } + } +} + +#[derive(Clone, Copy, Hash, Debug, PartialEq)] +pub enum PAuthKey { + A, + B, +} + +#[derive(Clone, Copy, Hash, Debug, PartialEq)] +pub struct PacRet { + pub leaf: bool, + pub key: PAuthKey, +} + +#[derive(Clone, Copy, Hash, Debug, PartialEq)] +pub struct BranchProtection { + pub bti: bool, + pub pac_ret: Option<PacRet>, +} + +impl Default for BranchProtection { + fn default() -> Self { + BranchProtection { bti: false, pac_ret: None } + } +} + +pub const fn default_lib_output() -> CrateType { + CrateType::Rlib +} + +fn default_configuration(sess: &Session) -> CrateConfig { + // NOTE: This should be kept in sync with `CrateCheckConfig::fill_well_known` below. + let end = &sess.target.endian; + let arch = &sess.target.arch; + let wordsz = sess.target.pointer_width.to_string(); + let os = &sess.target.os; + let env = &sess.target.env; + let abi = &sess.target.abi; + let vendor = &sess.target.vendor; + let min_atomic_width = sess.target.min_atomic_width(); + let max_atomic_width = sess.target.max_atomic_width(); + let atomic_cas = sess.target.atomic_cas; + let layout = TargetDataLayout::parse(&sess.target).unwrap_or_else(|err| { + sess.fatal(&err); + }); + + let mut ret = FxHashSet::default(); + ret.reserve(7); // the minimum number of insertions + // Target bindings. + ret.insert((sym::target_os, Some(Symbol::intern(os)))); + for fam in sess.target.families.as_ref() { + ret.insert((sym::target_family, Some(Symbol::intern(fam)))); + if fam == "windows" { + ret.insert((sym::windows, None)); + } else if fam == "unix" { + ret.insert((sym::unix, None)); + } + } + ret.insert((sym::target_arch, Some(Symbol::intern(arch)))); + ret.insert((sym::target_endian, Some(Symbol::intern(end.as_str())))); + ret.insert((sym::target_pointer_width, Some(Symbol::intern(&wordsz)))); + ret.insert((sym::target_env, Some(Symbol::intern(env)))); + ret.insert((sym::target_abi, Some(Symbol::intern(abi)))); + ret.insert((sym::target_vendor, Some(Symbol::intern(vendor)))); + if sess.target.has_thread_local { + ret.insert((sym::target_thread_local, None)); + } + for (i, align) in [ + (8, layout.i8_align.abi), + (16, layout.i16_align.abi), + (32, layout.i32_align.abi), + (64, layout.i64_align.abi), + (128, layout.i128_align.abi), + ] { + if i >= min_atomic_width && i <= max_atomic_width { + let mut insert_atomic = |s, align: Align| { + ret.insert((sym::target_has_atomic_load_store, Some(Symbol::intern(s)))); + if atomic_cas { + ret.insert((sym::target_has_atomic, Some(Symbol::intern(s)))); + } + if align.bits() == i { + ret.insert((sym::target_has_atomic_equal_alignment, Some(Symbol::intern(s)))); + } + }; + let s = i.to_string(); + insert_atomic(&s, align); + if s == wordsz { + insert_atomic("ptr", layout.pointer_align.abi); + } + } + } + + let panic_strategy = sess.panic_strategy(); + ret.insert((sym::panic, Some(panic_strategy.desc_symbol()))); + + for s in sess.opts.unstable_opts.sanitizer { + let symbol = Symbol::intern(&s.to_string()); + ret.insert((sym::sanitize, Some(symbol))); + } + + if sess.opts.debug_assertions { + ret.insert((sym::debug_assertions, None)); + } + // JUSTIFICATION: before wrapper fn is available + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + if sess.opts.crate_types.contains(&CrateType::ProcMacro) { + ret.insert((sym::proc_macro, None)); + } + ret +} + +/// Converts the crate `cfg!` configuration from `String` to `Symbol`. +/// `rustc_interface::interface::Config` accepts this in the compiler configuration, +/// but the symbol interner is not yet set up then, so we must convert it later. +pub fn to_crate_config(cfg: FxHashSet<(String, Option<String>)>) -> CrateConfig { + cfg.into_iter().map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))).collect() +} + +/// The parsed `--check-cfg` options +pub struct CheckCfg<T = String> { + /// The set of all `names()`, if None no name checking is performed + pub names_valid: Option<FxHashSet<T>>, + /// Is well known values activated + pub well_known_values: bool, + /// The set of all `values()` + pub values_valid: FxHashMap<T, FxHashSet<T>>, +} + +impl<T> Default for CheckCfg<T> { + fn default() -> Self { + CheckCfg { + names_valid: Default::default(), + values_valid: Default::default(), + well_known_values: false, + } + } +} + +impl<T> CheckCfg<T> { + fn map_data<O: Eq + Hash>(&self, f: impl Fn(&T) -> O) -> CheckCfg<O> { + CheckCfg { + names_valid: self + .names_valid + .as_ref() + .map(|names_valid| names_valid.iter().map(|a| f(a)).collect()), + values_valid: self + .values_valid + .iter() + .map(|(a, b)| (f(a), b.iter().map(|b| f(b)).collect())) + .collect(), + well_known_values: self.well_known_values, + } + } +} + +/// Converts the crate `--check-cfg` options from `String` to `Symbol`. +/// `rustc_interface::interface::Config` accepts this in the compiler configuration, +/// but the symbol interner is not yet set up then, so we must convert it later. +pub fn to_crate_check_config(cfg: CheckCfg) -> CrateCheckConfig { + cfg.map_data(|s| Symbol::intern(s)) +} + +impl CrateCheckConfig { + /// Fills a `CrateCheckConfig` with well-known configuration names. + fn fill_well_known_names(&mut self) { + // NOTE: This should be kept in sync with `default_configuration` and + // `fill_well_known_values` + const WELL_KNOWN_NAMES: &[Symbol] = &[ + // rustc + sym::unix, + sym::windows, + sym::target_os, + sym::target_family, + sym::target_arch, + sym::target_endian, + sym::target_pointer_width, + sym::target_env, + sym::target_abi, + sym::target_vendor, + sym::target_thread_local, + sym::target_has_atomic_load_store, + sym::target_has_atomic, + sym::target_has_atomic_equal_alignment, + sym::target_feature, + sym::panic, + sym::sanitize, + sym::debug_assertions, + sym::proc_macro, + sym::test, + sym::feature, + // rustdoc + sym::doc, + sym::doctest, + // miri + sym::miri, + ]; + + // We only insert well-known names if `names()` was activated + if let Some(names_valid) = &mut self.names_valid { + names_valid.extend(WELL_KNOWN_NAMES); + } + } + + /// Fills a `CrateCheckConfig` with well-known configuration values. + fn fill_well_known_values(&mut self) { + if !self.well_known_values { + return; + } + + // NOTE: This should be kept in sync with `default_configuration` and + // `fill_well_known_names` + + let panic_values = &PanicStrategy::all(); + + let atomic_values = &[ + sym::ptr, + sym::integer(8usize), + sym::integer(16usize), + sym::integer(32usize), + sym::integer(64usize), + sym::integer(128usize), + ]; + + let sanitize_values = SanitizerSet::all() + .into_iter() + .map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap())); + + // Unknown possible values: + // - `feature` + // - `target_feature` + + // No-values + for name in [ + sym::doc, + sym::miri, + sym::unix, + sym::test, + sym::doctest, + sym::windows, + sym::proc_macro, + sym::debug_assertions, + sym::target_thread_local, + ] { + self.values_valid.entry(name).or_default(); + } + + // Pre-defined values + self.values_valid.entry(sym::panic).or_default().extend(panic_values); + self.values_valid.entry(sym::sanitize).or_default().extend(sanitize_values); + self.values_valid.entry(sym::target_has_atomic).or_default().extend(atomic_values); + self.values_valid + .entry(sym::target_has_atomic_load_store) + .or_default() + .extend(atomic_values); + self.values_valid + .entry(sym::target_has_atomic_equal_alignment) + .or_default() + .extend(atomic_values); + + // Target specific values + { + const VALUES: [&Symbol; 8] = [ + &sym::target_os, + &sym::target_family, + &sym::target_arch, + &sym::target_endian, + &sym::target_env, + &sym::target_abi, + &sym::target_vendor, + &sym::target_pointer_width, + ]; + + // Initialize (if not already initialized) + for &e in VALUES { + self.values_valid.entry(e).or_default(); + } + + // Get all values map at once otherwise it would be costly. + // (8 values * 220 targets ~= 1760 times, at the time of writing this comment). + let [ + values_target_os, + values_target_family, + values_target_arch, + values_target_endian, + values_target_env, + values_target_abi, + values_target_vendor, + values_target_pointer_width, + ] = self + .values_valid + .get_many_mut(VALUES) + .expect("unable to get all the check-cfg values buckets"); + + for target in TARGETS + .iter() + .map(|target| Target::expect_builtin(&TargetTriple::from_triple(target))) + { + values_target_os.insert(Symbol::intern(&target.options.os)); + values_target_family + .extend(target.options.families.iter().map(|family| Symbol::intern(family))); + values_target_arch.insert(Symbol::intern(&target.arch)); + values_target_endian.insert(Symbol::intern(&target.options.endian.as_str())); + values_target_env.insert(Symbol::intern(&target.options.env)); + values_target_abi.insert(Symbol::intern(&target.options.abi)); + values_target_vendor.insert(Symbol::intern(&target.options.vendor)); + values_target_pointer_width.insert(sym::integer(target.pointer_width)); + } + } + } + + pub fn fill_well_known(&mut self) { + self.fill_well_known_names(); + self.fill_well_known_values(); + } +} + +pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig { + // Combine the configuration requested by the session (command line) with + // some default and generated configuration items. + let default_cfg = default_configuration(sess); + // If the user wants a test runner, then add the test cfg. + if sess.opts.test { + user_cfg.insert((sym::test, None)); + } + user_cfg.extend(default_cfg.iter().cloned()); + user_cfg +} + +pub(super) fn build_target_config( + opts: &Options, + target_override: Option<Target>, + sysroot: &Path, +) -> Target { + let target_result = target_override.map_or_else( + || Target::search(&opts.target_triple, sysroot), + |t| Ok((t, TargetWarnings::empty())), + ); + let (target, target_warnings) = target_result.unwrap_or_else(|e| { + early_error( + opts.error_format, + &format!( + "Error loading target specification: {}. \ + Run `rustc --print target-list` for a list of built-in targets", + e + ), + ) + }); + for warning in target_warnings.warning_messages() { + early_warn(opts.error_format, &warning) + } + + if !matches!(target.pointer_width, 16 | 32 | 64) { + early_error( + opts.error_format, + &format!( + "target specification was invalid: \ + unrecognized target-pointer-width {}", + target.pointer_width + ), + ) + } + + target +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum OptionStability { + Stable, + Unstable, +} + +pub struct RustcOptGroup { + pub apply: Box<dyn Fn(&mut getopts::Options) -> &mut getopts::Options>, + pub name: &'static str, + pub stability: OptionStability, +} + +impl RustcOptGroup { + pub fn is_stable(&self) -> bool { + self.stability == OptionStability::Stable + } + + pub fn stable<F>(name: &'static str, f: F) -> RustcOptGroup + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Stable } + } + + pub fn unstable<F>(name: &'static str, f: F) -> RustcOptGroup + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Unstable } + } +} + +// The `opt` local module holds wrappers around the `getopts` API that +// adds extra rustc-specific metadata to each option; such metadata +// is exposed by . The public +// functions below ending with `_u` are the functions that return +// *unstable* options, i.e., options that are only enabled when the +// user also passes the `-Z unstable-options` debugging flag. +mod opt { + // The `fn flag*` etc below are written so that we can use them + // in the future; do not warn about them not being used right now. + #![allow(dead_code)] + + use super::RustcOptGroup; + + pub type R = RustcOptGroup; + pub type S = &'static str; + + fn stable<F>(name: S, f: F) -> R + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup::stable(name, f) + } + + fn unstable<F>(name: S, f: F) -> R + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup::unstable(name, f) + } + + fn longer(a: S, b: S) -> S { + if a.len() > b.len() { a } else { b } + } + + pub fn opt_s(a: S, b: S, c: S, d: S) -> R { + stable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) + } + pub fn multi_s(a: S, b: S, c: S, d: S) -> R { + stable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) + } + pub fn flag_s(a: S, b: S, c: S) -> R { + stable(longer(a, b), move |opts| opts.optflag(a, b, c)) + } + pub fn flagmulti_s(a: S, b: S, c: S) -> R { + stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) + } + + pub fn opt(a: S, b: S, c: S, d: S) -> R { + unstable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) + } + pub fn multi(a: S, b: S, c: S, d: S) -> R { + unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) + } +} + +/// Returns the "short" subset of the rustc command line options, +/// including metadata for each option, such as whether the option is +/// part of the stable long-term interface for rustc. +pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> { + vec![ + opt::flag_s("h", "help", "Display this message"), + opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"), + opt::multi("", "check-cfg", "Provide list of valid cfg options for checking", "SPEC"), + opt::multi_s( + "L", + "", + "Add a directory to the library search path. The + optional KIND can be one of dependency, crate, native, + framework, or all (the default).", + "[KIND=]PATH", + ), + opt::multi_s( + "l", + "", + "Link the generated crate(s) to the specified native + library NAME. The optional KIND can be one of + static, framework, or dylib (the default). + Optional comma separated MODIFIERS (bundle|verbatim|whole-archive|as-needed) + may be specified each with a prefix of either '+' to + enable or '-' to disable.", + "[KIND[:MODIFIERS]=]NAME[:RENAME]", + ), + make_crate_type_option(), + opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"), + opt::opt_s( + "", + "edition", + "Specify which edition of the compiler to use when compiling code.", + EDITION_NAME_LIST, + ), + opt::multi_s( + "", + "emit", + "Comma separated list of types of output for \ + the compiler to emit", + "[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]", + ), + opt::multi_s( + "", + "print", + "Compiler information to print on stdout", + "[crate-name|file-names|sysroot|target-libdir|cfg|target-list|\ + target-cpus|target-features|relocation-models|code-models|\ + tls-models|target-spec-json|native-static-libs|stack-protector-strategies|\ + link-args]", + ), + opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), + opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), + opt::opt_s("o", "", "Write output to <filename>", "FILENAME"), + opt::opt_s( + "", + "out-dir", + "Write output to compiler-chosen filename \ + in <dir>", + "DIR", + ), + opt::opt_s( + "", + "explain", + "Provide a detailed explanation of an error \ + message", + "OPT", + ), + opt::flag_s("", "test", "Build a test harness"), + opt::opt_s("", "target", "Target triple for which the code is compiled", "TARGET"), + opt::multi_s("A", "allow", "Set lint allowed", "LINT"), + opt::multi_s("W", "warn", "Set lint warnings", "LINT"), + opt::multi_s("", "force-warn", "Set lint force-warn", "LINT"), + opt::multi_s("D", "deny", "Set lint denied", "LINT"), + opt::multi_s("F", "forbid", "Set lint forbidden", "LINT"), + opt::multi_s( + "", + "cap-lints", + "Set the most restrictive lint level. \ + More restrictive lints are capped at this \ + level", + "LEVEL", + ), + opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), + opt::flag_s("V", "version", "Print version info and exit"), + opt::flag_s("v", "verbose", "Use verbose output"), + ] +} + +/// Returns all rustc command line options, including metadata for +/// each option, such as whether the option is part of the stable +/// long-term interface for rustc. +pub fn rustc_optgroups() -> Vec<RustcOptGroup> { + let mut opts = rustc_short_optgroups(); + // FIXME: none of these descriptions are actually used + opts.extend(vec![ + opt::multi_s( + "", + "extern", + "Specify where an external rust library is located", + "NAME[=PATH]", + ), + opt::opt_s("", "sysroot", "Override the system root", "PATH"), + opt::multi("Z", "", "Set unstable / perma-unstable options", "FLAG"), + opt::opt_s( + "", + "error-format", + "How errors and other messages are produced", + "human|json|short", + ), + opt::multi_s("", "json", "Configure the JSON output of the compiler", "CONFIG"), + opt::opt_s( + "", + "color", + "Configure coloring of output: + auto = colorize, if output goes to a tty (default); + always = always colorize output; + never = never colorize output", + "auto|always|never", + ), + opt::opt_s( + "", + "diagnostic-width", + "Inform rustc of the width of the output so that diagnostics can be truncated to fit", + "WIDTH", + ), + opt::multi_s( + "", + "remap-path-prefix", + "Remap source names in all output (compiler messages and output files)", + "FROM=TO", + ), + ]); + opts +} + +pub fn get_cmd_lint_options( + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) { + let mut lint_opts_with_position = vec![]; + let mut describe_lints = false; + + for level in [lint::Allow, lint::Warn, lint::ForceWarn(None), lint::Deny, lint::Forbid] { + for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) { + if lint_name == "help" { + describe_lints = true; + } else { + lint_opts_with_position.push((arg_pos, lint_name.replace('-', "_"), level)); + } + } + } + + lint_opts_with_position.sort_by_key(|x| x.0); + let lint_opts = lint_opts_with_position + .iter() + .cloned() + .map(|(_, lint_name, level)| (lint_name, level)) + .collect(); + + let lint_cap = matches.opt_str("cap-lints").map(|cap| { + lint::Level::from_str(&cap) + .unwrap_or_else(|| early_error(error_format, &format!("unknown lint level: `{cap}`"))) + }); + + (lint_opts, describe_lints, lint_cap) +} + +/// Parses the `--color` flag. +pub fn parse_color(matches: &getopts::Matches) -> ColorConfig { + match matches.opt_str("color").as_ref().map(|s| &s[..]) { + Some("auto") => ColorConfig::Auto, + Some("always") => ColorConfig::Always, + Some("never") => ColorConfig::Never, + + None => ColorConfig::Auto, + + Some(arg) => early_error( + ErrorOutputType::default(), + &format!( + "argument for `--color` must be auto, \ + always or never (instead was `{arg}`)" + ), + ), + } +} + +/// Possible json config files +pub struct JsonConfig { + pub json_rendered: HumanReadableErrorType, + pub json_artifact_notifications: bool, + pub json_unused_externs: JsonUnusedExterns, + pub json_future_incompat: bool, +} + +/// Report unused externs in event stream +#[derive(Copy, Clone)] +pub enum JsonUnusedExterns { + /// Do not + No, + /// Report, but do not exit with failure status for deny/forbid + Silent, + /// Report, and also exit with failure status for deny/forbid + Loud, +} + +impl JsonUnusedExterns { + pub fn is_enabled(&self) -> bool { + match self { + JsonUnusedExterns::No => false, + JsonUnusedExterns::Loud | JsonUnusedExterns::Silent => true, + } + } + + pub fn is_loud(&self) -> bool { + match self { + JsonUnusedExterns::No | JsonUnusedExterns::Silent => false, + JsonUnusedExterns::Loud => true, + } + } +} + +/// Parse the `--json` flag. +/// +/// The first value returned is how to render JSON diagnostics, and the second +/// is whether or not artifact notifications are enabled. +pub fn parse_json(matches: &getopts::Matches) -> JsonConfig { + let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = + HumanReadableErrorType::Default; + let mut json_color = ColorConfig::Never; + let mut json_artifact_notifications = false; + let mut json_unused_externs = JsonUnusedExterns::No; + let mut json_future_incompat = false; + for option in matches.opt_strs("json") { + // For now conservatively forbid `--color` with `--json` since `--json` + // won't actually be emitting any colors and anything colorized is + // embedded in a diagnostic message anyway. + if matches.opt_str("color").is_some() { + early_error( + ErrorOutputType::default(), + "cannot specify the `--color` option with `--json`", + ); + } + + for sub_option in option.split(',') { + match sub_option { + "diagnostic-short" => json_rendered = HumanReadableErrorType::Short, + "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, + "artifacts" => json_artifact_notifications = true, + "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud, + "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent, + "future-incompat" => json_future_incompat = true, + s => early_error( + ErrorOutputType::default(), + &format!("unknown `--json` option `{s}`"), + ), + } + } + } + + JsonConfig { + json_rendered: json_rendered(json_color), + json_artifact_notifications, + json_unused_externs, + json_future_incompat, + } +} + +/// Parses the `--error-format` flag. +pub fn parse_error_format( + matches: &getopts::Matches, + color: ColorConfig, + json_rendered: HumanReadableErrorType, +) -> ErrorOutputType { + // We need the `opts_present` check because the driver will send us Matches + // with only stable options if no unstable options are used. Since error-format + // is unstable, it will not be present. We have to use `opts_present` not + // `opt_present` because the latter will panic. + let error_format = if matches.opts_present(&["error-format".to_owned()]) { + match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { + None | Some("human") => { + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + } + Some("human-annotate-rs") => { + ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color)) + } + Some("json") => ErrorOutputType::Json { pretty: false, json_rendered }, + Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, + Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), + + Some(arg) => early_error( + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)), + &format!( + "argument for `--error-format` must be `human`, `json` or \ + `short` (instead was `{arg}`)" + ), + ), + } + } else { + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + }; + + match error_format { + ErrorOutputType::Json { .. } => {} + + // Conservatively require that the `--json` argument is coupled with + // `--error-format=json`. This means that `--json` is specified we + // should actually be emitting JSON blobs. + _ if !matches.opt_strs("json").is_empty() => { + early_error( + ErrorOutputType::default(), + "using `--json` requires also using `--error-format=json`", + ); + } + + _ => {} + } + + error_format +} + +pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition { + let edition = match matches.opt_str("edition") { + Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| { + early_error( + ErrorOutputType::default(), + &format!( + "argument for `--edition` must be one of: \ + {EDITION_NAME_LIST}. (instead was `{arg}`)" + ), + ) + }), + None => DEFAULT_EDITION, + }; + + if !edition.is_stable() && !nightly_options::is_unstable_enabled(matches) { + let is_nightly = nightly_options::match_is_nightly_build(matches); + let msg = if !is_nightly { + format!( + "the crate requires edition {}, but the latest edition supported by this Rust version is {}", + edition, LATEST_STABLE_EDITION + ) + } else { + format!("edition {edition} is unstable and only available with -Z unstable-options") + }; + early_error(ErrorOutputType::default(), &msg) + } + + edition +} + +fn check_error_format_stability( + unstable_opts: &UnstableOptions, + error_format: ErrorOutputType, + json_rendered: HumanReadableErrorType, +) { + if !unstable_opts.unstable_options { + if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format { + early_error( + ErrorOutputType::Json { pretty: false, json_rendered }, + "`--error-format=pretty-json` is unstable", + ); + } + if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = + error_format + { + early_error( + ErrorOutputType::Json { pretty: false, json_rendered }, + "`--error-format=human-annotate-rs` is unstable", + ); + } + } +} + +fn parse_output_types( + unstable_opts: &UnstableOptions, + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> OutputTypes { + let mut output_types = BTreeMap::new(); + if !unstable_opts.parse_only { + for list in matches.opt_strs("emit") { + for output_type in list.split(',') { + let (shorthand, path) = match output_type.split_once('=') { + None => (output_type, None), + Some((shorthand, path)) => (shorthand, Some(PathBuf::from(path))), + }; + let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { + early_error( + error_format, + &format!( + "unknown emission type: `{shorthand}` - expected one of: {display}", + display = OutputType::shorthands_display(), + ), + ) + }); + output_types.insert(output_type, path); + } + } + }; + if output_types.is_empty() { + output_types.insert(OutputType::Exe, None); + } + OutputTypes(output_types) +} + +fn should_override_cgus_and_disable_thinlto( + output_types: &OutputTypes, + matches: &getopts::Matches, + error_format: ErrorOutputType, + mut codegen_units: Option<usize>, +) -> (bool, Option<usize>) { + let mut disable_thinlto = false; + // Issue #30063: if user requests LLVM-related output to one + // particular path, disable codegen-units. + let incompatible: Vec<_> = output_types + .0 + .iter() + .map(|ot_path| ot_path.0) + .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file()) + .map(|ot| ot.shorthand()) + .collect(); + if !incompatible.is_empty() { + match codegen_units { + Some(n) if n > 1 => { + if matches.opt_present("o") { + for ot in &incompatible { + early_warn( + error_format, + &format!( + "`--emit={ot}` with `-o` incompatible with \ + `-C codegen-units=N` for N > 1", + ), + ); + } + early_warn(error_format, "resetting to default -C codegen-units=1"); + codegen_units = Some(1); + disable_thinlto = true; + } + } + _ => { + codegen_units = Some(1); + disable_thinlto = true; + } + } + } + + if codegen_units == Some(0) { + early_error(error_format, "value for codegen units must be a positive non-zero integer"); + } + + (disable_thinlto, codegen_units) +} + +fn check_thread_count(unstable_opts: &UnstableOptions, error_format: ErrorOutputType) { + if unstable_opts.threads == 0 { + early_error(error_format, "value for threads must be a positive non-zero integer"); + } + + if unstable_opts.threads > 1 && unstable_opts.fuel.is_some() { + early_error(error_format, "optimization fuel is incompatible with multiple threads"); + } +} + +fn collect_print_requests( + cg: &mut CodegenOptions, + unstable_opts: &mut UnstableOptions, + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> Vec<PrintRequest> { + let mut prints = Vec::<PrintRequest>::new(); + if cg.target_cpu.as_ref().map_or(false, |s| s == "help") { + prints.push(PrintRequest::TargetCPUs); + cg.target_cpu = None; + }; + if cg.target_feature == "help" { + prints.push(PrintRequest::TargetFeatures); + cg.target_feature = String::new(); + } + + prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s { + "crate-name" => PrintRequest::CrateName, + "file-names" => PrintRequest::FileNames, + "sysroot" => PrintRequest::Sysroot, + "target-libdir" => PrintRequest::TargetLibdir, + "cfg" => PrintRequest::Cfg, + "target-list" => PrintRequest::TargetList, + "target-cpus" => PrintRequest::TargetCPUs, + "target-features" => PrintRequest::TargetFeatures, + "relocation-models" => PrintRequest::RelocationModels, + "code-models" => PrintRequest::CodeModels, + "tls-models" => PrintRequest::TlsModels, + "native-static-libs" => PrintRequest::NativeStaticLibs, + "stack-protector-strategies" => PrintRequest::StackProtectorStrategies, + "target-spec-json" => { + if unstable_opts.unstable_options { + PrintRequest::TargetSpec + } else { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to \ + enable the target-spec-json print option", + ); + } + } + "link-args" => PrintRequest::LinkArgs, + req => early_error(error_format, &format!("unknown print request `{req}`")), + })); + + prints +} + +pub fn parse_target_triple( + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> TargetTriple { + match matches.opt_str("target") { + Some(target) if target.ends_with(".json") => { + let path = Path::new(&target); + TargetTriple::from_path(&path).unwrap_or_else(|_| { + early_error(error_format, &format!("target file {path:?} does not exist")) + }) + } + Some(target) => TargetTriple::TargetTriple(target), + _ => TargetTriple::from_triple(host_triple()), + } +} + +fn parse_opt_level( + matches: &getopts::Matches, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> OptLevel { + // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able + // to use them interchangeably. However, because they're technically different flags, + // we need to work out manually which should take precedence if both are supplied (i.e. + // the rightmost flag). We do this by finding the (rightmost) position of both flags and + // comparing them. Note that if a flag is not found, its position will be `None`, which + // always compared less than `Some(_)`. + let max_o = matches.opt_positions("O").into_iter().max(); + let max_c = matches + .opt_strs_pos("C") + .into_iter() + .flat_map(|(i, s)| { + // NB: This can match a string without `=`. + if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None } + }) + .max(); + if max_o > max_c { + OptLevel::Default + } else { + match cg.opt_level.as_ref() { + "0" => OptLevel::No, + "1" => OptLevel::Less, + "2" => OptLevel::Default, + "3" => OptLevel::Aggressive, + "s" => OptLevel::Size, + "z" => OptLevel::SizeMin, + arg => { + early_error( + error_format, + &format!( + "optimization level needs to be \ + between 0-3, s or z (instead was `{arg}`)" + ), + ); + } + } + } +} + +fn select_debuginfo( + matches: &getopts::Matches, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> DebugInfo { + let max_g = matches.opt_positions("g").into_iter().max(); + let max_c = matches + .opt_strs_pos("C") + .into_iter() + .flat_map(|(i, s)| { + // NB: This can match a string without `=`. + if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None } + }) + .max(); + if max_g > max_c { + DebugInfo::Full + } else { + match cg.debuginfo { + 0 => DebugInfo::None, + 1 => DebugInfo::Limited, + 2 => DebugInfo::Full, + arg => { + early_error( + error_format, + &format!( + "debug info level needs to be between \ + 0-2 (instead was `{arg}`)" + ), + ); + } + } + } +} + +pub(crate) fn parse_assert_incr_state( + opt_assertion: &Option<String>, + error_format: ErrorOutputType, +) -> Option<IncrementalStateAssertion> { + match opt_assertion { + Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded), + Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded), + Some(s) => { + early_error(error_format, &format!("unexpected incremental state assertion value: {s}")) + } + None => None, + } +} + +fn parse_native_lib_kind( + matches: &getopts::Matches, + kind: &str, + error_format: ErrorOutputType, +) -> (NativeLibKind, Option<bool>) { + let (kind, modifiers) = match kind.split_once(':') { + None => (kind, None), + Some((kind, modifiers)) => (kind, Some(modifiers)), + }; + + let kind = match kind { + "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, + "dylib" => NativeLibKind::Dylib { as_needed: None }, + "framework" => NativeLibKind::Framework { as_needed: None }, + "link-arg" => { + if !nightly_options::is_unstable_enabled(matches) { + let why = if nightly_options::match_is_nightly_build(matches) { + " and only accepted on the nightly compiler" + } else { + ", the `-Z unstable-options` flag must also be passed to use it" + }; + early_error(error_format, &format!("library kind `link-arg` is unstable{why}")) + } + NativeLibKind::LinkArg + } + _ => early_error( + error_format, + &format!( + "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg" + ), + ), + }; + match modifiers { + None => (kind, None), + Some(modifiers) => parse_native_lib_modifiers(kind, modifiers, error_format, matches), + } +} + +fn parse_native_lib_modifiers( + mut kind: NativeLibKind, + modifiers: &str, + error_format: ErrorOutputType, + matches: &getopts::Matches, +) -> (NativeLibKind, Option<bool>) { + let mut verbatim = None; + for modifier in modifiers.split(',') { + let (modifier, value) = match modifier.strip_prefix(&['+', '-']) { + Some(m) => (m, modifier.starts_with('+')), + None => early_error( + error_format, + "invalid linking modifier syntax, expected '+' or '-' prefix \ + before one of: bundle, verbatim, whole-archive, as-needed", + ), + }; + + let report_unstable_modifier = || { + if !nightly_options::is_unstable_enabled(matches) { + let why = if nightly_options::match_is_nightly_build(matches) { + " and only accepted on the nightly compiler" + } else { + ", the `-Z unstable-options` flag must also be passed to use it" + }; + early_error( + error_format, + &format!("linking modifier `{modifier}` is unstable{why}"), + ) + } + }; + let assign_modifier = |dst: &mut Option<bool>| { + if dst.is_some() { + let msg = format!("multiple `{modifier}` modifiers in a single `-l` option"); + early_error(error_format, &msg) + } else { + *dst = Some(value); + } + }; + match (modifier, &mut kind) { + ("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle), + ("bundle", _) => early_error( + error_format, + "linking modifier `bundle` is only compatible with `static` linking kind", + ), + + ("verbatim", _) => { + report_unstable_modifier(); + assign_modifier(&mut verbatim) + } + + ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => { + assign_modifier(whole_archive) + } + ("whole-archive", _) => early_error( + error_format, + "linking modifier `whole-archive` is only compatible with `static` linking kind", + ), + + ("as-needed", NativeLibKind::Dylib { as_needed }) + | ("as-needed", NativeLibKind::Framework { as_needed }) => { + report_unstable_modifier(); + assign_modifier(as_needed) + } + ("as-needed", _) => early_error( + error_format, + "linking modifier `as-needed` is only compatible with \ + `dylib` and `framework` linking kinds", + ), + + // Note: this error also excludes the case with empty modifier + // string, like `modifiers = ""`. + _ => early_error( + error_format, + &format!( + "unknown linking modifier `{modifier}`, expected one \ + of: bundle, verbatim, whole-archive, as-needed" + ), + ), + } + } + + (kind, verbatim) +} + +fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<NativeLib> { + matches + .opt_strs("l") + .into_iter() + .map(|s| { + // Parse string of the form "[KIND[:MODIFIERS]=]lib[:new_name]", + // where KIND is one of "dylib", "framework", "static", "link-arg" and + // where MODIFIERS are a comma separated list of supported modifiers + // (bundle, verbatim, whole-archive, as-needed). Each modifier is prefixed + // with either + or - to indicate whether it is enabled or disabled. + // The last value specified for a given modifier wins. + let (name, kind, verbatim) = match s.split_once('=') { + None => (s, NativeLibKind::Unspecified, None), + Some((kind, name)) => { + let (kind, verbatim) = parse_native_lib_kind(matches, kind, error_format); + (name.to_string(), kind, verbatim) + } + }; + + let (name, new_name) = match name.split_once(':') { + None => (name, None), + Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())), + }; + if name.is_empty() { + early_error(error_format, "library name must not be empty"); + } + NativeLib { name, new_name, kind, verbatim } + }) + .collect() +} + +pub fn parse_externs( + matches: &getopts::Matches, + unstable_opts: &UnstableOptions, + error_format: ErrorOutputType, +) -> Externs { + let is_unstable_enabled = unstable_opts.unstable_options; + let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new(); + for arg in matches.opt_strs("extern") { + let (name, path) = match arg.split_once('=') { + None => (arg, None), + Some((name, path)) => (name.to_string(), Some(Path::new(path))), + }; + let (options, name) = match name.split_once(':') { + None => (None, name), + Some((opts, name)) => (Some(opts), name.to_string()), + }; + + let path = path.map(|p| CanonicalizedPath::new(p)); + + let entry = externs.entry(name.to_owned()); + + use std::collections::btree_map::Entry; + + let entry = if let Some(path) = path { + // --extern prelude_name=some_file.rlib + match entry { + Entry::Vacant(vacant) => { + let files = BTreeSet::from_iter(iter::once(path)); + vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files))) + } + Entry::Occupied(occupied) => { + let ext_ent = occupied.into_mut(); + match ext_ent { + ExternEntry { location: ExternLocation::ExactPaths(files), .. } => { + files.insert(path); + } + ExternEntry { + location: location @ ExternLocation::FoundInLibrarySearchDirectories, + .. + } => { + // Exact paths take precedence over search directories. + let files = BTreeSet::from_iter(iter::once(path)); + *location = ExternLocation::ExactPaths(files); + } + } + ext_ent + } + } + } else { + // --extern prelude_name + match entry { + Entry::Vacant(vacant) => { + vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories)) + } + Entry::Occupied(occupied) => { + // Ignore if already specified. + occupied.into_mut() + } + } + }; + + let mut is_private_dep = false; + let mut add_prelude = true; + let mut nounused_dep = false; + if let Some(opts) = options { + if !is_unstable_enabled { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to \ + enable `--extern options", + ); + } + for opt in opts.split(',') { + match opt { + "priv" => is_private_dep = true, + "noprelude" => { + if let ExternLocation::ExactPaths(_) = &entry.location { + add_prelude = false; + } else { + early_error( + error_format, + "the `noprelude` --extern option requires a file path", + ); + } + } + "nounused" => nounused_dep = true, + _ => early_error(error_format, &format!("unknown --extern option `{opt}`")), + } + } + } + + // Crates start out being not private, and go to being private `priv` + // is specified. + entry.is_private_dep |= is_private_dep; + // likewise `nounused` + entry.nounused_dep |= nounused_dep; + // If any flag is missing `noprelude`, then add to the prelude. + entry.add_prelude |= add_prelude; + } + Externs(externs) +} + +fn parse_remap_path_prefix( + matches: &getopts::Matches, + unstable_opts: &UnstableOptions, + error_format: ErrorOutputType, +) -> Vec<(PathBuf, PathBuf)> { + let mut mapping: Vec<(PathBuf, PathBuf)> = matches + .opt_strs("remap-path-prefix") + .into_iter() + .map(|remap| match remap.rsplit_once('=') { + None => early_error( + error_format, + "--remap-path-prefix must contain '=' between FROM and TO", + ), + Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)), + }) + .collect(); + match &unstable_opts.remap_cwd_prefix { + Some(to) => match std::env::current_dir() { + Ok(cwd) => mapping.push((cwd, to.clone())), + Err(_) => (), + }, + None => (), + }; + mapping +} + +// JUSTIFICATION: before wrapper fn is available +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] +pub fn build_session_options(matches: &getopts::Matches) -> Options { + let color = parse_color(matches); + + let edition = parse_crate_edition(matches); + + let JsonConfig { + json_rendered, + json_artifact_notifications, + json_unused_externs, + json_future_incompat, + } = parse_json(matches); + + let error_format = parse_error_format(matches, color, json_rendered); + + let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| { + early_error(error_format, "`--diagnostic-width` must be an positive integer"); + }); + + let unparsed_crate_types = matches.opt_strs("crate-type"); + let crate_types = parse_crate_types_from_list(unparsed_crate_types) + .unwrap_or_else(|e| early_error(error_format, &e)); + + let mut unstable_opts = UnstableOptions::build(matches, error_format); + let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); + + check_error_format_stability(&unstable_opts, error_format, json_rendered); + + if !unstable_opts.unstable_options && json_unused_externs.is_enabled() { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to enable \ + the flag `--json=unused-externs`", + ); + } + + let output_types = parse_output_types(&unstable_opts, matches, error_format); + + let mut cg = CodegenOptions::build(matches, error_format); + let (disable_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto( + &output_types, + matches, + error_format, + cg.codegen_units, + ); + + check_thread_count(&unstable_opts, error_format); + + let incremental = cg.incremental.as_ref().map(PathBuf::from); + + let assert_incr_state = parse_assert_incr_state(&unstable_opts.assert_incr_state, error_format); + + if unstable_opts.profile && incremental.is_some() { + early_error( + error_format, + "can't instrument with gcov profiling when compiling incrementally", + ); + } + if unstable_opts.profile { + match codegen_units { + Some(1) => {} + None => codegen_units = Some(1), + Some(_) => early_error( + error_format, + "can't instrument with gcov profiling with multiple codegen units", + ), + } + } + + if cg.profile_generate.enabled() && cg.profile_use.is_some() { + early_error( + error_format, + "options `-C profile-generate` and `-C profile-use` are exclusive", + ); + } + + if unstable_opts.profile_sample_use.is_some() + && (cg.profile_generate.enabled() || cg.profile_use.is_some()) + { + early_error( + error_format, + "option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`", + ); + } + + // Handle both `-Z symbol-mangling-version` and `-C symbol-mangling-version`; the latter takes + // precedence. + match (cg.symbol_mangling_version, unstable_opts.symbol_mangling_version) { + (Some(smv_c), Some(smv_z)) if smv_c != smv_z => { + early_error( + error_format, + "incompatible values passed for `-C symbol-mangling-version` \ + and `-Z symbol-mangling-version`", + ); + } + (Some(SymbolManglingVersion::V0), _) => {} + (Some(_), _) if !unstable_opts.unstable_options => { + early_error( + error_format, + "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`", + ); + } + (None, None) => {} + (None, smv) => { + early_warn( + error_format, + "`-Z symbol-mangling-version` is deprecated; use `-C symbol-mangling-version`", + ); + cg.symbol_mangling_version = smv; + } + _ => {} + } + + // Handle both `-Z instrument-coverage` and `-C instrument-coverage`; the latter takes + // precedence. + match (cg.instrument_coverage, unstable_opts.instrument_coverage) { + (Some(ic_c), Some(ic_z)) if ic_c != ic_z => { + early_error( + error_format, + "incompatible values passed for `-C instrument-coverage` \ + and `-Z instrument-coverage`", + ); + } + (Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {} + (Some(_), _) if !unstable_opts.unstable_options => { + early_error( + error_format, + "`-C instrument-coverage=except-*` requires `-Z unstable-options`", + ); + } + (None, None) => {} + (None, ic) => { + early_warn( + error_format, + "`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`", + ); + cg.instrument_coverage = ic; + } + _ => {} + } + + if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) { + if cg.profile_generate.enabled() || cg.profile_use.is_some() { + early_error( + error_format, + "option `-C instrument-coverage` is not compatible with either `-C profile-use` \ + or `-C profile-generate`", + ); + } + + // `-C instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent + // and reversible name mangling. Note, LLVM coverage tools can analyze coverage over + // multiple runs, including some changes to source code; so mangled names must be consistent + // across compilations. + match cg.symbol_mangling_version { + None => cg.symbol_mangling_version = Some(SymbolManglingVersion::V0), + Some(SymbolManglingVersion::Legacy) => { + early_warn( + error_format, + "-C instrument-coverage requires symbol mangling version `v0`, \ + but `-C symbol-mangling-version=legacy` was specified", + ); + } + Some(SymbolManglingVersion::V0) => {} + } + } + + if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") { + unstable_opts.graphviz_font = graphviz_font; + } + + if !cg.embed_bitcode { + match cg.lto { + LtoCli::No | LtoCli::Unspecified => {} + LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => early_error( + error_format, + "options `-C embed-bitcode=no` and `-C lto` are incompatible", + ), + } + } + + if cg.linker_flavor == Some(LinkerFlavor::L4Bender) + && !nightly_options::is_unstable_enabled(matches) + { + early_error( + error_format, + "`l4-bender` linker flavor is unstable, `-Z unstable-options` \ + flag must also be passed to explicitly use it", + ); + } + + let prints = collect_print_requests(&mut cg, &mut unstable_opts, matches, error_format); + + let cg = cg; + + let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); + let target_triple = parse_target_triple(matches, error_format); + let opt_level = parse_opt_level(matches, &cg, error_format); + // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able + // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) + // for more details. + let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No); + let debuginfo = select_debuginfo(matches, &cg, error_format); + + let mut search_paths = vec![]; + for s in &matches.opt_strs("L") { + search_paths.push(SearchPath::from_cli_opt(&s, error_format)); + } + + let libs = parse_libs(matches, error_format); + + let test = matches.opt_present("test"); + + if !cg.remark.is_empty() && debuginfo == DebugInfo::None { + early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations"); + } + + let externs = parse_externs(matches, &unstable_opts, error_format); + + let crate_name = matches.opt_str("crate-name"); + + let remap_path_prefix = parse_remap_path_prefix(matches, &unstable_opts, error_format); + + let pretty = parse_pretty(&unstable_opts, error_format); + + if !unstable_opts.unstable_options + && !target_triple.triple().contains("apple") + && cg.split_debuginfo.is_some() + { + early_error(error_format, "`-Csplit-debuginfo` is unstable on this platform"); + } + + // Try to find a directory containing the Rust `src`, for more details see + // the doc comment on the `real_rust_source_base_dir` field. + let tmp_buf; + let sysroot = match &sysroot_opt { + Some(s) => s, + None => { + tmp_buf = crate::filesearch::get_or_default_sysroot(); + &tmp_buf + } + }; + let real_rust_source_base_dir = { + // This is the location used by the `rust-src` `rustup` component. + let mut candidate = sysroot.join("lib/rustlib/src/rust"); + if let Ok(metadata) = candidate.symlink_metadata() { + // Replace the symlink rustbuild creates, with its destination. + // We could try to use `fs::canonicalize` instead, but that might + // produce unnecessarily verbose path. + if metadata.file_type().is_symlink() { + if let Ok(symlink_dest) = std::fs::read_link(&candidate) { + candidate = symlink_dest; + } + } + } + + // Only use this directory if it has a file we can expect to always find. + if candidate.join("library/std/src/lib.rs").is_file() { Some(candidate) } else { None } + }; + + let working_dir = std::env::current_dir().unwrap_or_else(|e| { + early_error(error_format, &format!("Current directory is invalid: {e}")); + }); + + let (path, remapped) = + FilePathMapping::new(remap_path_prefix.clone()).map_prefix(working_dir.clone()); + let working_dir = if remapped { + RealFileName::Remapped { local_path: Some(working_dir), virtual_name: path } + } else { + RealFileName::LocalPath(path) + }; + + Options { + assert_incr_state, + crate_types, + optimize: opt_level, + debuginfo, + lint_opts, + lint_cap, + describe_lints, + output_types, + search_paths, + maybe_sysroot: sysroot_opt, + target_triple, + test, + incremental, + unstable_opts, + prints, + cg, + error_format, + diagnostic_width, + externs, + unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()), + crate_name, + libs, + debug_assertions, + actually_rustdoc: false, + trimmed_def_paths: TrimmedDefPaths::default(), + cli_forced_codegen_units: codegen_units, + cli_forced_thinlto_off: disable_thinlto, + remap_path_prefix, + real_rust_source_base_dir, + edition, + json_artifact_notifications, + json_unused_externs, + json_future_incompat, + pretty, + working_dir, + } +} + +fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Option<PpMode> { + use PpMode::*; + + let first = match unstable_opts.unpretty.as_deref()? { + "normal" => Source(PpSourceMode::Normal), + "identified" => Source(PpSourceMode::Identified), + "expanded" => Source(PpSourceMode::Expanded), + "expanded,identified" => Source(PpSourceMode::ExpandedIdentified), + "expanded,hygiene" => Source(PpSourceMode::ExpandedHygiene), + "ast-tree" => AstTree(PpAstTreeMode::Normal), + "ast-tree,expanded" => AstTree(PpAstTreeMode::Expanded), + "hir" => Hir(PpHirMode::Normal), + "hir,identified" => Hir(PpHirMode::Identified), + "hir,typed" => Hir(PpHirMode::Typed), + "hir-tree" => HirTree, + "thir-tree" => ThirTree, + "mir" => Mir, + "mir-cfg" => MirCFG, + name => early_error( + efmt, + &format!( + "argument to `unpretty` must be one of `normal`, `identified`, \ + `expanded`, `expanded,identified`, `expanded,hygiene`, \ + `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \ + `hir,typed`, `hir-tree`, `thir-tree`, `mir` or `mir-cfg`; got {name}" + ), + ), + }; + tracing::debug!("got unpretty option: {first:?}"); + Some(first) +} + +pub fn make_crate_type_option() -> RustcOptGroup { + opt::multi_s( + "", + "crate-type", + "Comma separated list of types of crates + for the compiler to emit", + "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]", + ) +} + +pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> { + let mut crate_types: Vec<CrateType> = Vec::new(); + for unparsed_crate_type in &list_list { + for part in unparsed_crate_type.split(',') { + let new_part = match part { + "lib" => default_lib_output(), + "rlib" => CrateType::Rlib, + "staticlib" => CrateType::Staticlib, + "dylib" => CrateType::Dylib, + "cdylib" => CrateType::Cdylib, + "bin" => CrateType::Executable, + "proc-macro" => CrateType::ProcMacro, + _ => return Err(format!("unknown crate type: `{part}`")), + }; + if !crate_types.contains(&new_part) { + crate_types.push(new_part) + } + } + } + + Ok(crate_types) +} + +pub mod nightly_options { + use super::{ErrorOutputType, OptionStability, RustcOptGroup}; + use crate::early_error; + use rustc_feature::UnstableFeatures; + + pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool { + match_is_nightly_build(matches) + && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options") + } + + pub fn match_is_nightly_build(matches: &getopts::Matches) -> bool { + is_nightly_build(matches.opt_str("crate-name").as_deref()) + } + + pub fn is_nightly_build(krate: Option<&str>) -> bool { + UnstableFeatures::from_environment(krate).is_nightly_build() + } + + pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) { + let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options"); + let really_allows_unstable_options = match_is_nightly_build(matches); + + for opt in flags.iter() { + if opt.stability == OptionStability::Stable { + continue; + } + if !matches.opt_present(opt.name) { + continue; + } + if opt.name != "Z" && !has_z_unstable_option { + early_error( + ErrorOutputType::default(), + &format!( + "the `-Z unstable-options` flag must also be passed to enable \ + the flag `{}`", + opt.name + ), + ); + } + if really_allows_unstable_options { + continue; + } + match opt.stability { + OptionStability::Unstable => { + let msg = format!( + "the option `{}` is only accepted on the \ + nightly compiler", + opt.name + ); + early_error(ErrorOutputType::default(), &msg); + } + OptionStability::Stable => {} + } + } + } +} + +impl fmt::Display for CrateType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + CrateType::Executable => "bin".fmt(f), + CrateType::Dylib => "dylib".fmt(f), + CrateType::Rlib => "rlib".fmt(f), + CrateType::Staticlib => "staticlib".fmt(f), + CrateType::Cdylib => "cdylib".fmt(f), + CrateType::ProcMacro => "proc-macro".fmt(f), + } + } +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum PpSourceMode { + /// `-Zunpretty=normal` + Normal, + /// `-Zunpretty=expanded` + Expanded, + /// `-Zunpretty=identified` + Identified, + /// `-Zunpretty=expanded,identified` + ExpandedIdentified, + /// `-Zunpretty=expanded,hygiene` + ExpandedHygiene, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum PpAstTreeMode { + /// `-Zunpretty=ast` + Normal, + /// `-Zunpretty=ast,expanded` + Expanded, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum PpHirMode { + /// `-Zunpretty=hir` + Normal, + /// `-Zunpretty=hir,identified` + Identified, + /// `-Zunpretty=hir,typed` + Typed, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum PpMode { + /// Options that print the source code, i.e. + /// `-Zunpretty=normal` and `-Zunpretty=expanded` + Source(PpSourceMode), + AstTree(PpAstTreeMode), + /// Options that print the HIR, i.e. `-Zunpretty=hir` + Hir(PpHirMode), + /// `-Zunpretty=hir-tree` + HirTree, + /// `-Zunpretty=thir-tree` + ThirTree, + /// `-Zunpretty=mir` + Mir, + /// `-Zunpretty=mir-cfg` + MirCFG, +} + +impl PpMode { + pub fn needs_ast_map(&self) -> bool { + use PpMode::*; + use PpSourceMode::*; + match *self { + Source(Normal | Identified) | AstTree(PpAstTreeMode::Normal) => false, + + Source(Expanded | ExpandedIdentified | ExpandedHygiene) + | AstTree(PpAstTreeMode::Expanded) + | Hir(_) + | HirTree + | ThirTree + | Mir + | MirCFG => true, + } + } + pub fn needs_hir(&self) -> bool { + use PpMode::*; + match *self { + Source(_) | AstTree(_) => false, + + Hir(_) | HirTree | ThirTree | Mir | MirCFG => true, + } + } + + pub fn needs_analysis(&self) -> bool { + use PpMode::*; + matches!(*self, Mir | MirCFG | ThirTree) + } +} + +/// Command-line arguments passed to the compiler have to be incorporated with +/// the dependency tracking system for incremental compilation. This module +/// provides some utilities to make this more convenient. +/// +/// The values of all command-line arguments that are relevant for dependency +/// tracking are hashed into a single value that determines whether the +/// incremental compilation cache can be re-used or not. This hashing is done +/// via the `DepTrackingHash` trait defined below, since the standard `Hash` +/// implementation might not be suitable (e.g., arguments are stored in a `Vec`, +/// the hash of which is order dependent, but we might not want the order of +/// arguments to make a difference for the hash). +/// +/// However, since the value provided by `Hash::hash` often *is* suitable, +/// especially for primitive types, there is the +/// `impl_dep_tracking_hash_via_hash!()` macro that allows to simply reuse the +/// `Hash` implementation for `DepTrackingHash`. It's important though that +/// we have an opt-in scheme here, so one is hopefully forced to think about +/// how the hash should be calculated when adding a new command-line argument. +pub(crate) mod dep_tracking { + use super::{ + BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, + InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OomStrategy, OptLevel, + OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, SplitDwarfKind, + SwitchWithOptPath, SymbolManglingVersion, TrimmedDefPaths, + }; + use crate::lint; + use crate::options::WasiExecModel; + use crate::utils::{NativeLib, NativeLibKind}; + use rustc_errors::LanguageIdentifier; + use rustc_feature::UnstableFeatures; + use rustc_span::edition::Edition; + use rustc_span::RealFileName; + use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel}; + use rustc_target::spec::{ + RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, + }; + use std::collections::hash_map::DefaultHasher; + use std::collections::BTreeMap; + use std::hash::Hash; + use std::num::NonZeroUsize; + use std::path::PathBuf; + + pub trait DepTrackingHash { + fn hash( + &self, + hasher: &mut DefaultHasher, + error_format: ErrorOutputType, + for_crate_hash: bool, + ); + } + + macro_rules! impl_dep_tracking_hash_via_hash { + ($($t:ty),+ $(,)?) => {$( + impl DepTrackingHash for $t { + fn hash(&self, hasher: &mut DefaultHasher, _: ErrorOutputType, _for_crate_hash: bool) { + Hash::hash(self, hasher); + } + } + )+}; + } + + impl<T: DepTrackingHash> DepTrackingHash for Option<T> { + fn hash( + &self, + hasher: &mut DefaultHasher, + error_format: ErrorOutputType, + for_crate_hash: bool, + ) { + match self { + Some(x) => { + Hash::hash(&1, hasher); + DepTrackingHash::hash(x, hasher, error_format, for_crate_hash); + } + None => Hash::hash(&0, hasher), + } + } + } + + impl_dep_tracking_hash_via_hash!( + bool, + usize, + NonZeroUsize, + u64, + String, + PathBuf, + lint::Level, + WasiExecModel, + u32, + RelocModel, + CodeModel, + TlsModel, + InstrumentCoverage, + CrateType, + MergeFunctions, + PanicStrategy, + RelroLevel, + Passes, + OptLevel, + LtoCli, + DebugInfo, + UnstableFeatures, + NativeLib, + NativeLibKind, + SanitizerSet, + CFGuard, + CFProtection, + TargetTriple, + Edition, + LinkerPluginLto, + SplitDebuginfo, + SplitDwarfKind, + StackProtector, + SwitchWithOptPath, + SymbolManglingVersion, + SourceFileHashAlgorithm, + TrimmedDefPaths, + Option<LdImpl>, + OutputType, + RealFileName, + LocationDetail, + BranchProtection, + OomStrategy, + LanguageIdentifier, + ); + + impl<T1, T2> DepTrackingHash for (T1, T2) + where + T1: DepTrackingHash, + T2: DepTrackingHash, + { + fn hash( + &self, + hasher: &mut DefaultHasher, + error_format: ErrorOutputType, + for_crate_hash: bool, + ) { + Hash::hash(&0, hasher); + DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash); + Hash::hash(&1, hasher); + DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash); + } + } + + impl<T1, T2, T3> DepTrackingHash for (T1, T2, T3) + where + T1: DepTrackingHash, + T2: DepTrackingHash, + T3: DepTrackingHash, + { + fn hash( + &self, + hasher: &mut DefaultHasher, + error_format: ErrorOutputType, + for_crate_hash: bool, + ) { + Hash::hash(&0, hasher); + DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash); + Hash::hash(&1, hasher); + DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash); + Hash::hash(&2, hasher); + DepTrackingHash::hash(&self.2, hasher, error_format, for_crate_hash); + } + } + + impl<T: DepTrackingHash> DepTrackingHash for Vec<T> { + fn hash( + &self, + hasher: &mut DefaultHasher, + error_format: ErrorOutputType, + for_crate_hash: bool, + ) { + Hash::hash(&self.len(), hasher); + for (index, elem) in self.iter().enumerate() { + Hash::hash(&index, hasher); + DepTrackingHash::hash(elem, hasher, error_format, for_crate_hash); + } + } + } + + impl DepTrackingHash for OutputTypes { + fn hash( + &self, + hasher: &mut DefaultHasher, + error_format: ErrorOutputType, + for_crate_hash: bool, + ) { + Hash::hash(&self.0.len(), hasher); + for (key, val) in &self.0 { + DepTrackingHash::hash(key, hasher, error_format, for_crate_hash); + if !for_crate_hash { + DepTrackingHash::hash(val, hasher, error_format, for_crate_hash); + } + } + } + } + + // This is a stable hash because BTreeMap is a sorted container + pub(crate) fn stable_hash( + sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>, + hasher: &mut DefaultHasher, + error_format: ErrorOutputType, + for_crate_hash: bool, + ) { + for (key, sub_hash) in sub_hashes { + // Using Hash::hash() instead of DepTrackingHash::hash() is fine for + // the keys, as they are just plain strings + Hash::hash(&key.len(), hasher); + Hash::hash(key, hasher); + sub_hash.hash(hasher, error_format, for_crate_hash); + } + } +} + +/// Default behavior to use in out-of-memory situations. +#[derive(Clone, Copy, PartialEq, Hash, Debug, Encodable, Decodable, HashStable_Generic)] +pub enum OomStrategy { + /// Generate a panic that can be caught by `catch_unwind`. + Panic, + + /// Abort the process immediately. + Abort, +} + +impl OomStrategy { + pub const SYMBOL: &'static str = "__rust_alloc_error_handler_should_panic"; + + pub fn should_panic(self) -> u8 { + match self { + OomStrategy::Panic => 1, + OomStrategy::Abort => 0, + } + } +} + +/// How to run proc-macro code when building this crate +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum ProcMacroExecutionStrategy { + /// Run the proc-macro code on the same thread as the server. + SameThread, + + /// Run the proc-macro code on a different thread. + CrossThread, +} |