| //! Contains infrastructure for configuring the compiler, including parsing |
| //! command-line options. |
| |
| #![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable |
| |
| use std::collections::btree_map::{ |
| Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, |
| }; |
| use std::collections::{BTreeMap, BTreeSet}; |
| use std::ffi::OsStr; |
| use std::hash::Hash; |
| use std::path::{Path, PathBuf}; |
| use std::str::{self, FromStr}; |
| use std::sync::LazyLock; |
| use std::{cmp, fmt, fs, iter}; |
| |
| use externs::{ExternOpt, split_extern_opt}; |
| use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; |
| use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey}; |
| use rustc_errors::emitter::HumanReadableErrorType; |
| use rustc_errors::{ColorConfig, DiagArgValue, DiagCtxtFlags, IntoDiagArg}; |
| use rustc_feature::UnstableFeatures; |
| use rustc_macros::{Decodable, Encodable, HashStable_Generic}; |
| use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION}; |
| use rustc_span::source_map::FilePathMapping; |
| use rustc_span::{ |
| FileName, FileNameDisplayPreference, FileNameEmbeddablePreference, RealFileName, |
| SourceFileHashAlgorithm, Symbol, sym, |
| }; |
| use rustc_target::spec::{ |
| FramePointer, LinkSelfContainedComponents, LinkerFeatures, SplitDebuginfo, Target, TargetTuple, |
| }; |
| use tracing::debug; |
| |
| pub use crate::config::cfg::{Cfg, CheckCfg, ExpectedValues}; |
| use crate::config::native_libs::parse_native_libs; |
| use crate::errors::FileWriteFail; |
| pub use crate::options::*; |
| use crate::search_paths::SearchPath; |
| use crate::utils::CanonicalizedPath; |
| use crate::{EarlyDiagCtxt, HashStableContext, Session, filesearch, lint}; |
| |
| mod cfg; |
| mod externs; |
| mod native_libs; |
| pub mod sigpipe; |
| |
| pub const PRINT_KINDS: &[(&str, PrintKind)] = &[ |
| // tidy-alphabetical-start |
| ("all-target-specs-json", PrintKind::AllTargetSpecsJson), |
| ("calling-conventions", PrintKind::CallingConventions), |
| ("cfg", PrintKind::Cfg), |
| ("check-cfg", PrintKind::CheckCfg), |
| ("code-models", PrintKind::CodeModels), |
| ("crate-name", PrintKind::CrateName), |
| ("crate-root-lint-levels", PrintKind::CrateRootLintLevels), |
| ("deployment-target", PrintKind::DeploymentTarget), |
| ("file-names", PrintKind::FileNames), |
| ("host-tuple", PrintKind::HostTuple), |
| ("link-args", PrintKind::LinkArgs), |
| ("native-static-libs", PrintKind::NativeStaticLibs), |
| ("relocation-models", PrintKind::RelocationModels), |
| ("split-debuginfo", PrintKind::SplitDebuginfo), |
| ("stack-protector-strategies", PrintKind::StackProtectorStrategies), |
| ("supported-crate-types", PrintKind::SupportedCrateTypes), |
| ("sysroot", PrintKind::Sysroot), |
| ("target-cpus", PrintKind::TargetCPUs), |
| ("target-features", PrintKind::TargetFeatures), |
| ("target-libdir", PrintKind::TargetLibdir), |
| ("target-list", PrintKind::TargetList), |
| ("target-spec-json", PrintKind::TargetSpecJson), |
| ("tls-models", PrintKind::TlsModels), |
| // tidy-alphabetical-end |
| ]; |
| |
| /// 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 { |
| /// `-Copt-level=0` |
| No, |
| /// `-Copt-level=1` |
| Less, |
| /// `-Copt-level=2` |
| More, |
| /// `-Copt-level=3` / `-O` |
| Aggressive, |
| /// `-Copt-level=s` |
| Size, |
| /// `-Copt-level=z` |
| SizeMin, |
| } |
| |
| /// 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 `-C instrument-coverage` flag can have. |
| #[derive(Clone, Copy, PartialEq, Hash, Debug)] |
| pub enum InstrumentCoverage { |
| /// `-C instrument-coverage=no` (or `off`, `false` etc.) |
| No, |
| /// `-C instrument-coverage` or `-C instrument-coverage=yes` |
| Yes, |
| } |
| |
| /// Individual flag values controlled by `-Zcoverage-options`. |
| #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] |
| pub struct CoverageOptions { |
| pub level: CoverageLevel, |
| |
| /// `-Zcoverage-options=no-mir-spans`: Don't extract block coverage spans |
| /// from MIR statements/terminators, making it easier to inspect/debug |
| /// branch and MC/DC coverage mappings. |
| /// |
| /// For internal debugging only. If other code changes would make it hard |
| /// to keep supporting this flag, remove it. |
| pub no_mir_spans: bool, |
| |
| /// `-Zcoverage-options=discard-all-spans-in-codegen`: During codegen, |
| /// discard all coverage spans as though they were invalid. Needed by |
| /// regression tests for #133606, because we don't have an easy way to |
| /// reproduce it from actual source code. |
| pub discard_all_spans_in_codegen: bool, |
| } |
| |
| /// Controls whether branch coverage or MC/DC coverage is enabled. |
| #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] |
| pub enum CoverageLevel { |
| /// Instrument for coverage at the MIR block level. |
| #[default] |
| Block, |
| /// Also instrument branch points (includes block coverage). |
| Branch, |
| /// Same as branch coverage, but also adds branch instrumentation for |
| /// certain boolean expressions that are not directly used for branching. |
| /// |
| /// For example, in the following code, `b` does not directly participate |
| /// in a branch, but condition coverage will instrument it as its own |
| /// artificial branch: |
| /// ``` |
| /// # let (a, b) = (false, true); |
| /// let x = a && b; |
| /// // ^ last operand |
| /// ``` |
| /// |
| /// This level is mainly intended to be a stepping-stone towards full MC/DC |
| /// instrumentation, so it might be removed in the future when MC/DC is |
| /// sufficiently complete, or if it is making MC/DC changes difficult. |
| Condition, |
| /// Instrument for MC/DC. Mostly a superset of condition coverage, but might |
| /// differ in some corner cases. |
| Mcdc, |
| } |
| |
| // The different settings that the `-Z offload` flag can have. |
| #[derive(Clone, Copy, PartialEq, Hash, Debug)] |
| pub enum Offload { |
| /// Enable the llvm offload pipeline |
| Enable, |
| } |
| |
| /// The different settings that the `-Z autodiff` flag can have. |
| #[derive(Clone, PartialEq, Hash, Debug)] |
| pub enum AutoDiff { |
| /// Enable the autodiff opt pipeline |
| Enable, |
| |
| /// Print TypeAnalysis information |
| PrintTA, |
| /// Print TypeAnalysis information for a specific function |
| PrintTAFn(String), |
| /// Print ActivityAnalysis Information |
| PrintAA, |
| /// Print Performance Warnings from Enzyme |
| PrintPerf, |
| /// Print intermediate IR generation steps |
| PrintSteps, |
| /// Print the module, before running autodiff. |
| PrintModBefore, |
| /// Print the module after running autodiff. |
| PrintModAfter, |
| /// Print the module after running autodiff and optimizations. |
| PrintModFinal, |
| |
| /// Print all passes scheduled by LLVM |
| PrintPasses, |
| /// Disable extra opt run after running autodiff |
| NoPostopt, |
| /// Enzyme's loose type debug helper (can cause incorrect gradients!!) |
| /// Usable in cases where Enzyme errors with `can not deduce type of X`. |
| LooseTypes, |
| /// Runs Enzyme's aggressive inlining |
| Inline, |
| } |
| |
| /// Settings for `-Z instrument-xray` flag. |
| #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] |
| pub struct InstrumentXRay { |
| /// `-Z instrument-xray=always`, force instrumentation |
| pub always: bool, |
| /// `-Z instrument-xray=never`, disable instrumentation |
| pub never: bool, |
| /// `-Z instrument-xray=ignore-loops`, ignore presence of loops, |
| /// instrument functions based only on instruction count |
| pub ignore_loops: bool, |
| /// `-Z instrument-xray=instruction-threshold=N`, explicitly set instruction threshold |
| /// for instrumentation, or `None` to use compiler's default |
| pub instruction_threshold: Option<usize>, |
| /// `-Z instrument-xray=skip-entry`, do not instrument function entry |
| pub skip_entry: bool, |
| /// `-Z instrument-xray=skip-exit`, do not instrument function exit |
| pub skip_exit: bool, |
| } |
| |
| #[derive(Clone, PartialEq, Hash, Debug)] |
| pub enum LinkerPluginLto { |
| LinkerPlugin(PathBuf), |
| LinkerPluginAuto, |
| Disabled, |
| } |
| |
| impl LinkerPluginLto { |
| pub fn enabled(&self) -> bool { |
| match *self { |
| LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true, |
| LinkerPluginLto::Disabled => false, |
| } |
| } |
| } |
| |
| /// The different values `-C link-self-contained` can take: a list of individually enabled or |
| /// disabled components used during linking, coming from the rustc distribution, instead of being |
| /// found somewhere on the host system. |
| /// |
| /// They can be set in bulk via `-C link-self-contained=yes|y|on` or `-C |
| /// link-self-contained=no|n|off`, and those boolean values are the historical defaults. |
| /// |
| /// But each component is fine-grained, and can be unstably targeted, to use: |
| /// - some CRT objects |
| /// - the libc static library |
| /// - libgcc/libunwind libraries |
| /// - a linker we distribute |
| /// - some sanitizer runtime libraries |
| /// - all other MinGW libraries and Windows import libs |
| /// |
| #[derive(Default, Clone, PartialEq, Debug)] |
| pub struct LinkSelfContained { |
| /// Whether the user explicitly set `-C link-self-contained` on or off, the historical values. |
| /// Used for compatibility with the existing opt-in and target inference. |
| pub explicitly_set: Option<bool>, |
| |
| /// The components that are enabled on the CLI, using the `+component` syntax or one of the |
| /// `true` shortcuts. |
| enabled_components: LinkSelfContainedComponents, |
| |
| /// The components that are disabled on the CLI, using the `-component` syntax or one of the |
| /// `false` shortcuts. |
| disabled_components: LinkSelfContainedComponents, |
| } |
| |
| impl LinkSelfContained { |
| /// Incorporates an enabled or disabled component as specified on the CLI, if possible. |
| /// For example: `+linker`, and `-crto`. |
| pub(crate) fn handle_cli_component(&mut self, component: &str) -> Option<()> { |
| // Note that for example `-Cself-contained=y -Cself-contained=-linker` is not an explicit |
| // set of all values like `y` or `n` used to be. Therefore, if this flag had previously been |
| // set in bulk with its historical values, then manually setting a component clears that |
| // `explicitly_set` state. |
| if let Some(component_to_enable) = component.strip_prefix('+') { |
| self.explicitly_set = None; |
| self.enabled_components |
| .insert(LinkSelfContainedComponents::from_str(component_to_enable).ok()?); |
| Some(()) |
| } else if let Some(component_to_disable) = component.strip_prefix('-') { |
| self.explicitly_set = None; |
| self.disabled_components |
| .insert(LinkSelfContainedComponents::from_str(component_to_disable).ok()?); |
| Some(()) |
| } else { |
| None |
| } |
| } |
| |
| /// Turns all components on or off and records that this was done explicitly for compatibility |
| /// purposes. |
| pub(crate) fn set_all_explicitly(&mut self, enabled: bool) { |
| self.explicitly_set = Some(enabled); |
| |
| if enabled { |
| self.enabled_components = LinkSelfContainedComponents::all(); |
| self.disabled_components = LinkSelfContainedComponents::empty(); |
| } else { |
| self.enabled_components = LinkSelfContainedComponents::empty(); |
| self.disabled_components = LinkSelfContainedComponents::all(); |
| } |
| } |
| |
| /// Helper creating a fully enabled `LinkSelfContained` instance. Used in tests. |
| pub fn on() -> Self { |
| let mut on = LinkSelfContained::default(); |
| on.set_all_explicitly(true); |
| on |
| } |
| |
| /// To help checking CLI usage while some of the values are unstable: returns whether one of the |
| /// unstable components was set individually, for the given `TargetTuple`. This would also |
| /// require the `-Zunstable-options` flag, to be allowed. |
| fn check_unstable_variants(&self, target_tuple: &TargetTuple) -> Result<(), String> { |
| if self.explicitly_set.is_some() { |
| return Ok(()); |
| } |
| |
| // `-C link-self-contained=-linker` is only stable on x64 linux. |
| let has_minus_linker = self.disabled_components.is_linker_enabled(); |
| if has_minus_linker && target_tuple.tuple() != "x86_64-unknown-linux-gnu" { |
| return Err(format!( |
| "`-C link-self-contained=-linker` is unstable on the `{target_tuple}` \ |
| target. The `-Z unstable-options` flag must also be passed to use it on this target", |
| )); |
| } |
| |
| // Any `+linker` or other component used is unstable, and that's an error. |
| let unstable_enabled = self.enabled_components; |
| let unstable_disabled = self.disabled_components - LinkSelfContainedComponents::LINKER; |
| if !unstable_enabled.union(unstable_disabled).is_empty() { |
| return Err(String::from( |
| "only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` \ |
| are stable, the `-Z unstable-options` flag must also be passed to use \ |
| the unstable values", |
| )); |
| } |
| |
| Ok(()) |
| } |
| |
| /// Returns whether the self-contained linker component was enabled on the CLI, using the |
| /// `-C link-self-contained=+linker` syntax, or one of the `true` shortcuts. |
| pub fn is_linker_enabled(&self) -> bool { |
| self.enabled_components.contains(LinkSelfContainedComponents::LINKER) |
| } |
| |
| /// Returns whether the self-contained linker component was disabled on the CLI, using the |
| /// `-C link-self-contained=-linker` syntax, or one of the `false` shortcuts. |
| pub fn is_linker_disabled(&self) -> bool { |
| self.disabled_components.contains(LinkSelfContainedComponents::LINKER) |
| } |
| |
| /// Returns CLI inconsistencies to emit errors: individual components were both enabled and |
| /// disabled. |
| fn check_consistency(&self) -> Option<LinkSelfContainedComponents> { |
| if self.explicitly_set.is_some() { |
| None |
| } else { |
| let common = self.enabled_components.intersection(self.disabled_components); |
| if common.is_empty() { None } else { Some(common) } |
| } |
| } |
| } |
| |
| /// The different values that `-C linker-features` can take on the CLI: a list of individually |
| /// enabled or disabled features used during linking. |
| /// |
| /// There is no need to enable or disable them in bulk. Each feature is fine-grained, and can be |
| /// used to turn `LinkerFeatures` on or off, without needing to change the linker flavor: |
| /// - using the system lld, or the self-contained `rust-lld` linker |
| /// - using a C/C++ compiler to drive the linker (not yet exposed on the CLI) |
| /// - etc. |
| #[derive(Default, Copy, Clone, PartialEq, Debug)] |
| pub struct LinkerFeaturesCli { |
| /// The linker features that are enabled on the CLI, using the `+feature` syntax. |
| pub enabled: LinkerFeatures, |
| |
| /// The linker features that are disabled on the CLI, using the `-feature` syntax. |
| pub disabled: LinkerFeatures, |
| } |
| |
| impl LinkerFeaturesCli { |
| /// Accumulates an enabled or disabled feature as specified on the CLI, if possible. |
| /// For example: `+lld`, and `-lld`. |
| pub(crate) fn handle_cli_feature(&mut self, feature: &str) -> Option<()> { |
| // Duplicate flags are reduced as we go, the last occurrence wins: |
| // `+feature,-feature,+feature` only enables the feature, and does not record it as both |
| // enabled and disabled on the CLI. |
| // We also only expose `+/-lld` at the moment, as it's currently the only implemented linker |
| // feature and toggling `LinkerFeatures::CC` would be a noop. |
| match feature { |
| "+lld" => { |
| self.enabled.insert(LinkerFeatures::LLD); |
| self.disabled.remove(LinkerFeatures::LLD); |
| Some(()) |
| } |
| "-lld" => { |
| self.disabled.insert(LinkerFeatures::LLD); |
| self.enabled.remove(LinkerFeatures::LLD); |
| Some(()) |
| } |
| _ => None, |
| } |
| } |
| |
| /// When *not* using `-Z unstable-options` on the CLI, ensure only stable linker features are |
| /// used, for the given `TargetTuple`. Returns `Ok` if no unstable variants are used. |
| /// The caller should ensure that e.g. `nightly_options::is_unstable_enabled()` |
| /// returns false. |
| pub(crate) fn check_unstable_variants(&self, target_tuple: &TargetTuple) -> Result<(), String> { |
| // `-C linker-features=-lld` is only stable on x64 linux. |
| let has_minus_lld = self.disabled.is_lld_enabled(); |
| if has_minus_lld && target_tuple.tuple() != "x86_64-unknown-linux-gnu" { |
| return Err(format!( |
| "`-C linker-features=-lld` is unstable on the `{target_tuple}` \ |
| target. The `-Z unstable-options` flag must also be passed to use it on this target", |
| )); |
| } |
| |
| // Any `+lld` or non-lld feature used is unstable, and that's an error. |
| let unstable_enabled = self.enabled; |
| let unstable_disabled = self.disabled - LinkerFeatures::LLD; |
| if !unstable_enabled.union(unstable_disabled).is_empty() { |
| let unstable_features: Vec<_> = unstable_enabled |
| .iter() |
| .map(|f| format!("+{}", f.as_str().unwrap())) |
| .chain(unstable_disabled.iter().map(|f| format!("-{}", f.as_str().unwrap()))) |
| .collect(); |
| return Err(format!( |
| "`-C linker-features={}` is unstable, and also requires the \ |
| `-Z unstable-options` flag to be used", |
| unstable_features.join(","), |
| )); |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| /// 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, |
| } |
| |
| /// The different settings that can be enabled via the `-Z location-detail` flag. |
| #[derive(Copy, Clone, PartialEq, Hash, Debug)] |
| pub struct LocationDetail { |
| pub file: bool, |
| pub line: bool, |
| pub column: bool, |
| } |
| |
| impl LocationDetail { |
| pub(crate) fn all() -> Self { |
| Self { file: true, line: true, column: true } |
| } |
| } |
| |
| /// Values for the `-Z fmt-debug` flag. |
| #[derive(Copy, Clone, PartialEq, Hash, Debug)] |
| pub enum FmtDebug { |
| /// Derive fully-featured implementation |
| Full, |
| /// Print only type name, without fields |
| Shallow, |
| /// `#[derive(Debug)]` and `{:?}` are no-ops |
| None, |
| } |
| |
| impl FmtDebug { |
| pub(crate) fn all() -> [Symbol; 3] { |
| [sym::full, sym::none, sym::shallow] |
| } |
| } |
| |
| #[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, |
| Hashed, |
| } |
| |
| #[derive(Clone, Copy, Debug, PartialEq, Hash)] |
| pub enum DebugInfo { |
| None, |
| LineDirectivesOnly, |
| LineTablesOnly, |
| Limited, |
| Full, |
| } |
| |
| #[derive(Clone, Copy, Debug, PartialEq, Hash)] |
| pub enum DebugInfoCompression { |
| None, |
| Zlib, |
| Zstd, |
| } |
| |
| impl ToString for DebugInfoCompression { |
| fn to_string(&self) -> String { |
| match self { |
| DebugInfoCompression::None => "none", |
| DebugInfoCompression::Zlib => "zlib", |
| DebugInfoCompression::Zstd => "zstd", |
| } |
| .to_owned() |
| } |
| } |
| |
| #[derive(Clone, Copy, Debug, PartialEq, Hash)] |
| pub enum MirStripDebugInfo { |
| None, |
| LocalsInTinyFunctions, |
| AllLocals, |
| } |
| |
| /// 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(()), |
| }) |
| } |
| } |
| |
| macro_rules! define_output_types { |
| ( |
| $( |
| $(#[doc = $doc:expr])* |
| $Variant:ident => { |
| shorthand: $shorthand:expr, |
| extension: $extension:expr, |
| description: $description:expr, |
| default_filename: $default_filename:expr, |
| is_text: $is_text:expr, |
| compatible_with_cgus_and_single_output: $compatible:expr |
| } |
| ),* $(,)? |
| ) => { |
| #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)] |
| #[derive(Encodable, Decodable)] |
| pub enum OutputType { |
| $( |
| $(#[doc = $doc])* |
| $Variant, |
| )* |
| } |
| |
| |
| impl StableOrd for OutputType { |
| const CAN_USE_UNSTABLE_SORT: bool = true; |
| |
| // Trivial C-Style enums have a stable sort order across compilation sessions. |
| const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); |
| } |
| |
| impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType { |
| type KeyType = Self; |
| |
| fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { |
| *self |
| } |
| } |
| |
| |
| impl OutputType { |
| pub fn iter_all() -> impl Iterator<Item = OutputType> { |
| static ALL_VARIANTS: &[OutputType] = &[ |
| $( |
| OutputType::$Variant, |
| )* |
| ]; |
| ALL_VARIANTS.iter().copied() |
| } |
| |
| fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool { |
| match *self { |
| $( |
| OutputType::$Variant => $compatible, |
| )* |
| } |
| } |
| |
| pub fn shorthand(&self) -> &'static str { |
| match *self { |
| $( |
| OutputType::$Variant => $shorthand, |
| )* |
| } |
| } |
| |
| fn from_shorthand(shorthand: &str) -> Option<Self> { |
| match shorthand { |
| $( |
| s if s == $shorthand => Some(OutputType::$Variant), |
| )* |
| _ => None, |
| } |
| } |
| |
| fn shorthands_display() -> String { |
| let shorthands = vec![ |
| $( |
| format!("`{}`", $shorthand), |
| )* |
| ]; |
| shorthands.join(", ") |
| } |
| |
| pub fn extension(&self) -> &'static str { |
| match *self { |
| $( |
| OutputType::$Variant => $extension, |
| )* |
| } |
| } |
| |
| pub fn is_text_output(&self) -> bool { |
| match *self { |
| $( |
| OutputType::$Variant => $is_text, |
| )* |
| } |
| } |
| |
| pub fn description(&self) -> &'static str { |
| match *self { |
| $( |
| OutputType::$Variant => $description, |
| )* |
| } |
| } |
| |
| pub fn default_filename(&self) -> &'static str { |
| match *self { |
| $( |
| OutputType::$Variant => $default_filename, |
| )* |
| } |
| } |
| |
| |
| } |
| } |
| } |
| |
| define_output_types! { |
| Assembly => { |
| shorthand: "asm", |
| extension: "s", |
| description: "Generates a file with the crate's assembly code", |
| default_filename: "CRATE_NAME.s", |
| is_text: true, |
| compatible_with_cgus_and_single_output: false |
| }, |
| #[doc = "This is the optimized bitcode, which could be either pre-LTO or non-LTO bitcode,"] |
| #[doc = "depending on the specific request type."] |
| Bitcode => { |
| shorthand: "llvm-bc", |
| extension: "bc", |
| description: "Generates a binary file containing the LLVM bitcode", |
| default_filename: "CRATE_NAME.bc", |
| is_text: false, |
| compatible_with_cgus_and_single_output: false |
| }, |
| DepInfo => { |
| shorthand: "dep-info", |
| extension: "d", |
| description: "Generates a file with Makefile syntax that indicates all the source files that were loaded to generate the crate", |
| default_filename: "CRATE_NAME.d", |
| is_text: true, |
| compatible_with_cgus_and_single_output: true |
| }, |
| Exe => { |
| shorthand: "link", |
| extension: "", |
| description: "Generates the crates specified by --crate-type. This is the default if --emit is not specified", |
| default_filename: "(platform and crate-type dependent)", |
| is_text: false, |
| compatible_with_cgus_and_single_output: true |
| }, |
| LlvmAssembly => { |
| shorthand: "llvm-ir", |
| extension: "ll", |
| description: "Generates a file containing LLVM IR", |
| default_filename: "CRATE_NAME.ll", |
| is_text: true, |
| compatible_with_cgus_and_single_output: false |
| }, |
| Metadata => { |
| shorthand: "metadata", |
| extension: "rmeta", |
| description: "Generates a file containing metadata about the crate", |
| default_filename: "libCRATE_NAME.rmeta", |
| is_text: false, |
| compatible_with_cgus_and_single_output: true |
| }, |
| Mir => { |
| shorthand: "mir", |
| extension: "mir", |
| description: "Generates a file containing rustc's mid-level intermediate representation", |
| default_filename: "CRATE_NAME.mir", |
| is_text: true, |
| compatible_with_cgus_and_single_output: false |
| }, |
| Object => { |
| shorthand: "obj", |
| extension: "o", |
| description: "Generates a native object file", |
| default_filename: "CRATE_NAME.o", |
| is_text: false, |
| compatible_with_cgus_and_single_output: false |
| }, |
| #[doc = "This is the summary or index data part of the ThinLTO bitcode."] |
| ThinLinkBitcode => { |
| shorthand: "thin-link-bitcode", |
| extension: "indexing.o", |
| description: "Generates the ThinLTO summary as bitcode", |
| default_filename: "CRATE_NAME.indexing.o", |
| is_text: false, |
| compatible_with_cgus_and_single_output: false |
| }, |
| } |
| |
| /// The type of diagnostics output to generate. |
| #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] |
| pub enum ErrorOutputType { |
| /// Output meant for the consumption of humans. |
| #[default] |
| HumanReadable { |
| kind: HumanReadableErrorType = HumanReadableErrorType::Default, |
| color_config: ColorConfig = ColorConfig::Auto, |
| }, |
| /// 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, |
| color_config: ColorConfig, |
| }, |
| } |
| |
| #[derive(Clone, Hash, Debug)] |
| pub enum ResolveDocLinks { |
| /// Do not resolve doc links. |
| None, |
| /// Resolve doc links on exported items only for crate types that have metadata. |
| ExportedMetadata, |
| /// Resolve doc links on exported items. |
| Exported, |
| /// Resolve doc links on all items. |
| All, |
| } |
| |
| /// 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, Encodable, Decodable)] |
| pub struct OutputTypes(BTreeMap<OutputType, Option<OutFileName>>); |
| |
| impl OutputTypes { |
| pub fn new(entries: &[(OutputType, Option<OutFileName>)]) -> OutputTypes { |
| OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone())))) |
| } |
| |
| pub(crate) fn get(&self, key: &OutputType) -> Option<&Option<OutFileName>> { |
| self.0.get(key) |
| } |
| |
| pub fn contains_key(&self, key: &OutputType) -> bool { |
| self.0.contains_key(key) |
| } |
| |
| /// Returns `true` if user specified a name and not just produced type |
| pub fn contains_explicit_name(&self, key: &OutputType) -> bool { |
| matches!(self.0.get(key), Some(Some(..))) |
| } |
| |
| pub fn iter(&self) -> BTreeMapIter<'_, OutputType, Option<OutFileName>> { |
| self.0.iter() |
| } |
| |
| pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option<OutFileName>> { |
| self.0.keys() |
| } |
| |
| pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option<OutFileName>> { |
| 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::ThinLinkBitcode |
| | 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::ThinLinkBitcode |
| | 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, |
| /// If the extern entry is not referenced in the crate, force it to be resolved anyway. |
| /// |
| /// Allows a dependency satisfying, for instance, a missing panic handler to be injected |
| /// without modifying source: |
| /// `--extern force:extras=/path/to/lib/libstd.rlib` |
| pub force: 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() |
| } |
| } |
| |
| impl ExternEntry { |
| fn new(location: ExternLocation) -> ExternEntry { |
| ExternEntry { |
| location, |
| is_private_dep: false, |
| add_prelude: false, |
| nounused_dep: false, |
| force: false, |
| } |
| } |
| |
| pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> { |
| match &self.location { |
| ExternLocation::ExactPaths(set) => Some(set.iter()), |
| _ => None, |
| } |
| } |
| } |
| |
| #[derive(Clone, PartialEq, Debug)] |
| pub struct PrintRequest { |
| pub kind: PrintKind, |
| pub out: OutFileName, |
| } |
| |
| #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
| pub enum PrintKind { |
| // tidy-alphabetical-start |
| AllTargetSpecsJson, |
| CallingConventions, |
| Cfg, |
| CheckCfg, |
| CodeModels, |
| CrateName, |
| CrateRootLintLevels, |
| DeploymentTarget, |
| FileNames, |
| HostTuple, |
| LinkArgs, |
| NativeStaticLibs, |
| RelocationModels, |
| SplitDebuginfo, |
| StackProtectorStrategies, |
| SupportedCrateTypes, |
| Sysroot, |
| TargetCPUs, |
| TargetFeatures, |
| TargetLibdir, |
| TargetList, |
| TargetSpecJson, |
| TlsModels, |
| // tidy-alphabetical-end |
| } |
| |
| #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)] |
| pub struct NextSolverConfig { |
| /// Whether the new trait solver should be enabled in coherence. |
| pub coherence: bool = true, |
| /// Whether the new trait solver should be enabled everywhere. |
| /// This is only `true` if `coherence` is also enabled. |
| pub globally: bool = false, |
| } |
| |
| #[derive(Clone)] |
| 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 { |
| if let Input::File(ifile) = self { |
| // If for some reason getting the file stem as a UTF-8 string fails, |
| // then fallback to a fixed name. |
| if let Some(name) = ifile.file_stem().and_then(OsStr::to_str) { |
| return name; |
| } |
| } |
| "rust_out" |
| } |
| |
| pub fn source_name(&self) -> FileName { |
| match *self { |
| Input::File(ref ifile) => ifile.clone().into(), |
| Input::Str { ref name, .. } => name.clone(), |
| } |
| } |
| |
| pub fn opt_path(&self) -> Option<&Path> { |
| match self { |
| Input::File(file) => Some(file), |
| Input::Str { name, .. } => match name { |
| FileName::Real(real) => real.local_path(), |
| FileName::CfgSpec(_) => None, |
| FileName::Anon(_) => None, |
| FileName::MacroExpansion(_) => None, |
| FileName::ProcMacroSourceCode(_) => None, |
| FileName::CliCrateAttr(_) => None, |
| FileName::Custom(_) => None, |
| FileName::DocTest(path, _) => Some(path), |
| FileName::InlineAsm(_) => None, |
| }, |
| } |
| } |
| } |
| |
| #[derive(Clone, Hash, Debug, HashStable_Generic, PartialEq, Encodable, Decodable)] |
| pub enum OutFileName { |
| Real(PathBuf), |
| Stdout, |
| } |
| |
| impl OutFileName { |
| pub fn parent(&self) -> Option<&Path> { |
| match *self { |
| OutFileName::Real(ref path) => path.parent(), |
| OutFileName::Stdout => None, |
| } |
| } |
| |
| pub fn filestem(&self) -> Option<&OsStr> { |
| match *self { |
| OutFileName::Real(ref path) => path.file_stem(), |
| OutFileName::Stdout => Some(OsStr::new("stdout")), |
| } |
| } |
| |
| pub fn is_stdout(&self) -> bool { |
| match *self { |
| OutFileName::Real(_) => false, |
| OutFileName::Stdout => true, |
| } |
| } |
| |
| pub fn is_tty(&self) -> bool { |
| use std::io::IsTerminal; |
| match *self { |
| OutFileName::Real(_) => false, |
| OutFileName::Stdout => std::io::stdout().is_terminal(), |
| } |
| } |
| |
| pub fn as_path(&self) -> &Path { |
| match *self { |
| OutFileName::Real(ref path) => path.as_ref(), |
| OutFileName::Stdout => Path::new("stdout"), |
| } |
| } |
| |
| /// For a given output filename, return the actual name of the file that |
| /// can be used to write codegen data of type `flavor`. For real-path |
| /// output filenames, this would be trivial as we can just use the path. |
| /// Otherwise for stdout, return a temporary path so that the codegen data |
| /// may be later copied to stdout. |
| pub fn file_for_writing( |
| &self, |
| outputs: &OutputFilenames, |
| flavor: OutputType, |
| codegen_unit_name: &str, |
| invocation_temp: Option<&str>, |
| ) -> PathBuf { |
| match *self { |
| OutFileName::Real(ref path) => path.clone(), |
| OutFileName::Stdout => { |
| outputs.temp_path_for_cgu(flavor, codegen_unit_name, invocation_temp) |
| } |
| } |
| } |
| |
| pub fn overwrite(&self, content: &str, sess: &Session) { |
| match self { |
| OutFileName::Stdout => print!("{content}"), |
| OutFileName::Real(path) => { |
| if let Err(e) = fs::write(path, content) { |
| sess.dcx().emit_fatal(FileWriteFail { path, err: e.to_string() }); |
| } |
| } |
| } |
| } |
| } |
| |
| #[derive(Clone, Hash, Debug, HashStable_Generic, Encodable, Decodable)] |
| pub struct OutputFilenames { |
| pub(crate) out_directory: PathBuf, |
| /// Crate name. Never contains '-'. |
| crate_stem: String, |
| /// Typically based on `.rs` input file name. Any '-' is preserved. |
| filestem: String, |
| pub single_output_file: Option<OutFileName>, |
| 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_crate_name: String, |
| out_filestem: String, |
| single_output_file: Option<OutFileName>, |
| temps_directory: Option<PathBuf>, |
| extra: String, |
| outputs: OutputTypes, |
| ) -> Self { |
| OutputFilenames { |
| out_directory, |
| single_output_file, |
| temps_directory, |
| outputs, |
| crate_stem: format!("{out_crate_name}{extra}"), |
| filestem: format!("{out_filestem}{extra}"), |
| } |
| } |
| |
| pub fn path(&self, flavor: OutputType) -> OutFileName { |
| self.outputs |
| .get(&flavor) |
| .and_then(|p| p.to_owned()) |
| .or_else(|| self.single_output_file.clone()) |
| .unwrap_or_else(|| OutFileName::Real(self.output_path(flavor))) |
| } |
| |
| pub fn interface_path(&self) -> PathBuf { |
| self.out_directory.join(format!("lib{}.rs", self.crate_stem)) |
| } |
| |
| /// Gets the output path where a compilation artifact of the given type |
| /// should be placed on disk. |
| fn output_path(&self, flavor: OutputType) -> PathBuf { |
| let extension = flavor.extension(); |
| match flavor { |
| OutputType::Metadata => { |
| self.out_directory.join(format!("lib{}.{}", self.crate_stem, 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_for_cgu( |
| &self, |
| flavor: OutputType, |
| codegen_unit_name: &str, |
| invocation_temp: Option<&str>, |
| ) -> PathBuf { |
| let extension = flavor.extension(); |
| self.temp_path_ext_for_cgu(extension, codegen_unit_name, invocation_temp) |
| } |
| |
| /// Like `temp_path`, but specifically for dwarf objects. |
| pub fn temp_path_dwo_for_cgu( |
| &self, |
| codegen_unit_name: &str, |
| invocation_temp: Option<&str>, |
| ) -> PathBuf { |
| self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp) |
| } |
| |
| /// Like `temp_path`, but also supports things where there is no corresponding |
| /// OutputType, like noopt-bitcode or lto-bitcode. |
| pub fn temp_path_ext_for_cgu( |
| &self, |
| ext: &str, |
| codegen_unit_name: &str, |
| invocation_temp: Option<&str>, |
| ) -> PathBuf { |
| let mut extension = codegen_unit_name.to_string(); |
| |
| // Append `.{invocation_temp}` to ensure temporary files are unique. |
| if let Some(rng) = invocation_temp { |
| extension.push('.'); |
| extension.push_str(rng); |
| } |
| |
| // FIXME: This is sketchy that we're not appending `.rcgu` when the ext is empty. |
| // Append `.rcgu.{ext}`. |
| if !ext.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 temp_path_for_diagnostic(&self, ext: &str) -> PathBuf { |
| let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory); |
| self.with_directory_and_extension(temps_directory, &ext) |
| } |
| |
| pub fn with_extension(&self, extension: &str) -> PathBuf { |
| self.with_directory_and_extension(&self.out_directory, extension) |
| } |
| |
| pub fn with_directory_and_extension(&self, directory: &Path, 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: &str, |
| invocation_temp: Option<&str>, |
| ) -> Option<PathBuf> { |
| let obj_out = self.temp_path_for_cgu(OutputType::Object, cgu_name, invocation_temp); |
| let dwo_out = self.temp_path_dwo_for_cgu(cgu_name, invocation_temp); |
| 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) |
| } |
| } |
| } |
| } |
| |
| bitflags::bitflags! { |
| /// Scopes used to determined if it need to apply to --remap-path-prefix |
| #[derive(Clone, Copy, PartialEq, Eq, Hash)] |
| pub struct RemapPathScopeComponents: u8 { |
| /// Apply remappings to the expansion of std::file!() macro |
| const MACRO = 1 << 0; |
| /// Apply remappings to printed compiler diagnostics |
| const DIAGNOSTICS = 1 << 1; |
| /// Apply remappings to debug information |
| const DEBUGINFO = 1 << 3; |
| |
| /// An alias for `macro` and `debuginfo`. This ensures all paths in compiled |
| /// executables or libraries are remapped but not elsewhere. |
| const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits(); |
| } |
| } |
| |
| #[derive(Clone, Debug)] |
| pub struct Sysroot { |
| pub explicit: Option<PathBuf>, |
| pub default: PathBuf, |
| } |
| |
| impl Sysroot { |
| pub fn new(explicit: Option<PathBuf>) -> Sysroot { |
| Sysroot { explicit, default: filesearch::default_sysroot() } |
| } |
| |
| /// Return explicit sysroot if it was passed with `--sysroot`, or default sysroot otherwise. |
| pub fn path(&self) -> &Path { |
| self.explicit.as_deref().unwrap_or(&self.default) |
| } |
| |
| /// Returns both explicit sysroot if it was passed with `--sysroot` and the default sysroot. |
| pub fn all_paths(&self) -> impl Iterator<Item = &Path> { |
| self.explicit.as_deref().into_iter().chain(iter::once(&*self.default)) |
| } |
| } |
| |
| pub fn host_tuple() -> &'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") |
| } |
| |
| fn file_path_mapping( |
| remap_path_prefix: Vec<(PathBuf, PathBuf)>, |
| unstable_opts: &UnstableOptions, |
| ) -> FilePathMapping { |
| FilePathMapping::new( |
| remap_path_prefix.clone(), |
| if unstable_opts.remap_path_scope.contains(RemapPathScopeComponents::DIAGNOSTICS) |
| && !remap_path_prefix.is_empty() |
| { |
| FileNameDisplayPreference::Remapped |
| } else { |
| FileNameDisplayPreference::Local |
| }, |
| if unstable_opts.remap_path_scope.is_all() { |
| FileNameEmbeddablePreference::RemappedOnly |
| } else { |
| FileNameEmbeddablePreference::LocalAndRemapped |
| }, |
| ) |
| } |
| |
| impl Default for Options { |
| fn default() -> Options { |
| Options { |
| assert_incr_state: None, |
| crate_types: Vec::new(), |
| optimize: OptLevel::No, |
| debuginfo: DebugInfo::None, |
| debuginfo_compression: DebugInfoCompression::None, |
| lint_opts: Vec::new(), |
| lint_cap: None, |
| describe_lints: false, |
| output_types: OutputTypes(BTreeMap::new()), |
| search_paths: vec![], |
| sysroot: Sysroot::new(None), |
| target_triple: TargetTuple::from_tuple(host_tuple()), |
| test: false, |
| incremental: None, |
| untracked_state_hash: Default::default(), |
| 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, |
| resolve_doc_links: ResolveDocLinks::None, |
| trimmed_def_paths: false, |
| cli_forced_codegen_units: None, |
| cli_forced_local_thinlto_off: false, |
| remap_path_prefix: Vec::new(), |
| real_rust_source_base_dir: None, |
| real_rustc_dev_source_base_dir: None, |
| edition: DEFAULT_EDITION, |
| json_artifact_notifications: false, |
| json_timings: false, |
| json_unused_externs: JsonUnusedExterns::No, |
| json_future_incompat: false, |
| pretty: None, |
| working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()), |
| color: ColorConfig::Auto, |
| logical_env: FxIndexMap::default(), |
| verbose: false, |
| target_modifiers: BTreeMap::default(), |
| } |
| } |
| } |
| |
| 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 { |
| file_path_mapping(self.remap_path_prefix.clone(), &self.unstable_opts) |
| } |
| |
| /// Returns `true` if there will be an output file generated. |
| pub fn will_create_output_file(&self) -> bool { |
| !self.unstable_opts.parse_crate_root_only && // The file is just being parsed |
| self.unstable_opts.ls.is_empty() // 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::More | OptLevel::Aggressive => false, |
| }, |
| } |
| } |
| |
| pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion { |
| self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy) |
| } |
| } |
| |
| impl UnstableOptions { |
| pub fn dcx_flags(&self, can_emit_warnings: bool) -> DiagCtxtFlags { |
| DiagCtxtFlags { |
| can_emit_warnings, |
| treat_err_as_bug: self.treat_err_as_bug, |
| eagerly_emit_delayed_bugs: self.eagerly_emit_delayed_bugs, |
| macro_backtrace: self.macro_backtrace, |
| deduplicate_diagnostics: self.deduplicate_diagnostics, |
| track_diagnostics: self.track_diagnostics, |
| } |
| } |
| |
| pub fn src_hash_algorithm(&self, target: &Target) -> SourceFileHashAlgorithm { |
| self.src_hash_algorithm.unwrap_or_else(|| { |
| if target.is_like_msvc { |
| SourceFileHashAlgorithm::Sha256 |
| } else { |
| SourceFileHashAlgorithm::Md5 |
| } |
| }) |
| } |
| |
| pub fn checksum_hash_algorithm(&self) -> Option<SourceFileHashAlgorithm> { |
| self.checksum_hash_algorithm |
| } |
| } |
| |
| // 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 { |
| /// Specifies what to do with `SIGPIPE` before calling `fn main()`. |
| /// |
| /// What values that are valid and what they mean must be in sync |
| /// across rustc and libstd, but we don't want it public in libstd, |
| /// so we take a bit of an unusual approach with simple constants |
| /// and an `include!()`. |
| sigpipe: u8, |
| }, |
| } |
| |
| #[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] |
| #[derive(HashStable_Generic)] |
| pub enum CrateType { |
| Executable, |
| Dylib, |
| Rlib, |
| Staticlib, |
| Cdylib, |
| ProcMacro, |
| Sdylib, |
| } |
| |
| impl CrateType { |
| pub fn has_metadata(self) -> bool { |
| match self { |
| CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true, |
| CrateType::Executable |
| | CrateType::Cdylib |
| | CrateType::Staticlib |
| | CrateType::Sdylib => false, |
| } |
| } |
| } |
| |
| #[derive(Clone, Hash, Debug, PartialEq, Eq)] |
| pub enum Passes { |
| Some(Vec<String>), |
| All, |
| } |
| |
| impl Passes { |
| fn is_empty(&self) -> bool { |
| match *self { |
| Passes::Some(ref v) => v.is_empty(), |
| Passes::All => false, |
| } |
| } |
| |
| pub(crate) 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 pc: bool, |
| pub key: PAuthKey, |
| } |
| |
| #[derive(Clone, Copy, Hash, Debug, PartialEq, Default)] |
| pub struct BranchProtection { |
| pub bti: bool, |
| pub pac_ret: Option<PacRet>, |
| } |
| |
| pub(crate) const fn default_lib_output() -> CrateType { |
| CrateType::Rlib |
| } |
| |
| pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg { |
| // First disallow some configuration given on the command line |
| cfg::disallow_cfgs(sess, &user_cfg); |
| |
| // Then combine the configuration requested by the session (command line) with |
| // some default and generated configuration items. |
| user_cfg.extend(cfg::default_configuration(sess)); |
| user_cfg |
| } |
| |
| pub fn build_target_config( |
| early_dcx: &EarlyDiagCtxt, |
| target: &TargetTuple, |
| sysroot: &Path, |
| ) -> Target { |
| match Target::search(target, sysroot) { |
| Ok((target, warnings)) => { |
| for warning in warnings.warning_messages() { |
| early_dcx.early_warn(warning) |
| } |
| |
| if !matches!(target.pointer_width, 16 | 32 | 64) { |
| early_dcx.early_fatal(format!( |
| "target specification was invalid: unrecognized target-pointer-width {}", |
| target.pointer_width |
| )) |
| } |
| target |
| } |
| Err(e) => { |
| let mut err = |
| early_dcx.early_struct_fatal(format!("error loading target specification: {e}")); |
| err.help("run `rustc --print target-list` for a list of built-in targets"); |
| err.emit(); |
| } |
| } |
| } |
| |
| #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
| pub enum OptionStability { |
| Stable, |
| Unstable, |
| } |
| |
| #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
| pub enum OptionKind { |
| /// An option that takes a value, and cannot appear more than once (e.g. `--out-dir`). |
| /// |
| /// Corresponds to [`getopts::Options::optopt`]. |
| Opt, |
| |
| /// An option that takes a value, and can appear multiple times (e.g. `--emit`). |
| /// |
| /// Corresponds to [`getopts::Options::optmulti`]. |
| Multi, |
| |
| /// An option that does not take a value, and cannot appear more than once (e.g. `--help`). |
| /// |
| /// Corresponds to [`getopts::Options::optflag`]. |
| /// The `hint` string must be empty. |
| Flag, |
| |
| /// An option that does not take a value, and can appear multiple times (e.g. `-O`). |
| /// |
| /// Corresponds to [`getopts::Options::optflagmulti`]. |
| /// The `hint` string must be empty. |
| FlagMulti, |
| } |
| |
| pub struct RustcOptGroup { |
| /// The "primary" name for this option. Normally equal to `long_name`, |
| /// except for options that don't have a long name, in which case |
| /// `short_name` is used. |
| /// |
| /// This is needed when interacting with `getopts` in some situations, |
| /// because if an option has both forms, that library treats the long name |
| /// as primary and the short name as an alias. |
| pub name: &'static str, |
| stability: OptionStability, |
| kind: OptionKind, |
| |
| short_name: &'static str, |
| long_name: &'static str, |
| desc: &'static str, |
| value_hint: &'static str, |
| |
| /// If true, this option should not be printed by `rustc --help`, but |
| /// should still be printed by `rustc --help -v`. |
| pub is_verbose_help_only: bool, |
| } |
| |
| impl RustcOptGroup { |
| pub fn is_stable(&self) -> bool { |
| self.stability == OptionStability::Stable |
| } |
| |
| pub fn apply(&self, options: &mut getopts::Options) { |
| let &Self { short_name, long_name, desc, value_hint, .. } = self; |
| match self.kind { |
| OptionKind::Opt => options.optopt(short_name, long_name, desc, value_hint), |
| OptionKind::Multi => options.optmulti(short_name, long_name, desc, value_hint), |
| OptionKind::Flag => options.optflag(short_name, long_name, desc), |
| OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc), |
| }; |
| } |
| |
| /// This is for diagnostics-only. |
| pub fn long_name(&self) -> &str { |
| self.long_name |
| } |
| } |
| |
| pub fn make_opt( |
| stability: OptionStability, |
| kind: OptionKind, |
| short_name: &'static str, |
| long_name: &'static str, |
| desc: &'static str, |
| value_hint: &'static str, |
| ) -> RustcOptGroup { |
| // "Flag" options don't have a value, and therefore don't have a value hint. |
| match kind { |
| OptionKind::Opt | OptionKind::Multi => {} |
| OptionKind::Flag | OptionKind::FlagMulti => assert_eq!(value_hint, ""), |
| } |
| RustcOptGroup { |
| name: cmp::max_by_key(short_name, long_name, |s| s.len()), |
| stability, |
| kind, |
| short_name, |
| long_name, |
| desc, |
| value_hint, |
| is_verbose_help_only: false, |
| } |
| } |
| |
| static EDITION_STRING: LazyLock<String> = LazyLock::new(|| { |
| format!( |
| "Specify which edition of the compiler to use when compiling code. \ |
| The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE_EDITION}." |
| ) |
| }); |
| |
| static PRINT_HELP: LazyLock<String> = LazyLock::new(|| { |
| format!( |
| "Compiler information to print on stdout (or to a file)\n\ |
| INFO may be one of <{}>.", |
| PRINT_KINDS.iter().map(|(name, _)| format!("{name}")).collect::<Vec<_>>().join("|") |
| ) |
| }); |
| |
| static EMIT_HELP: LazyLock<String> = LazyLock::new(|| { |
| let mut result = |
| String::from("Comma separated list of types of output for the compiler to emit.\n"); |
| result.push_str("Each TYPE has the default FILE name:\n"); |
| |
| for output in OutputType::iter_all() { |
| result.push_str(&format!("* {} - {}\n", output.shorthand(), output.default_filename())); |
| } |
| |
| result |
| }); |
| |
| /// Returns all rustc command line options, including metadata for |
| /// each option, such as whether the option is stable. |
| /// |
| /// # Option style guidelines |
| /// |
| /// - `<param>`: Indicates a required parameter |
| /// - `[param]`: Indicates an optional parameter |
| /// - `|`: Indicates a mutually exclusive option |
| /// - `*`: a list element with description |
| pub fn rustc_optgroups() -> Vec<RustcOptGroup> { |
| use OptionKind::{Flag, FlagMulti, Multi, Opt}; |
| use OptionStability::{Stable, Unstable}; |
| |
| use self::make_opt as opt; |
| |
| let mut options = vec![ |
| opt(Stable, Flag, "h", "help", "Display this message", ""), |
| opt( |
| Stable, |
| Multi, |
| "", |
| "cfg", |
| "Configure the compilation environment.\n\ |
| SPEC supports the syntax `<NAME>[=\"<VALUE>\"]`.", |
| "<SPEC>", |
| ), |
| opt(Stable, Multi, "", "check-cfg", "Provide list of expected cfgs for checking", "<SPEC>"), |
| opt( |
| Stable, |
| Multi, |
| "L", |
| "", |
| "Add a directory to the library search path. \ |
| The optional KIND can be one of <dependency|crate|native|framework|all> (default: all).", |
| "[<KIND>=]<PATH>", |
| ), |
| opt( |
| Stable, |
| Multi, |
| "l", |
| "", |
| "Link the generated crate(s) to the specified native\n\ |
| library NAME. The optional KIND can be one of\n\ |
| <static|framework|dylib> (default: dylib).\n\ |
| Optional comma separated MODIFIERS\n\ |
| <bundle|verbatim|whole-archive|as-needed>\n\ |
| may be specified each with a prefix of either '+' to\n\ |
| enable or '-' to disable.", |
| "[<KIND>[:<MODIFIERS>]=]<NAME>[:<RENAME>]", |
| ), |
| make_crate_type_option(), |
| opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", "<NAME>"), |
| opt(Stable, Opt, "", "edition", &EDITION_STRING, EDITION_NAME_LIST), |
| opt(Stable, Multi, "", "emit", &EMIT_HELP, "<TYPE>[=<FILE>]"), |
| opt(Stable, Multi, "", "print", &PRINT_HELP, "<INFO>[=<FILE>]"), |
| opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""), |
| opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=3", ""), |
| opt(Stable, Opt, "o", "", "Write output to FILENAME", "<FILENAME>"), |
| opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in DIR", "<DIR>"), |
| opt( |
| Stable, |
| Opt, |
| "", |
| "explain", |
| "Provide a detailed explanation of an error message", |
| "<OPT>", |
| ), |
| opt(Stable, Flag, "", "test", "Build a test harness", ""), |
| opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", "<TARGET>"), |
| opt(Stable, Multi, "A", "allow", "Set lint allowed", "<LINT>"), |
| opt(Stable, Multi, "W", "warn", "Set lint warnings", "<LINT>"), |
| opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "<LINT>"), |
| opt(Stable, Multi, "D", "deny", "Set lint denied", "<LINT>"), |
| opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "<LINT>"), |
| opt( |
| Stable, |
| Multi, |
| "", |
| "cap-lints", |
| "Set the most restrictive lint level. More restrictive lints are capped at this level", |
| "<LEVEL>", |
| ), |
| opt(Stable, Multi, "C", "codegen", "Set a codegen option", "<OPT>[=<VALUE>]"), |
| opt(Stable, Flag, "V", "version", "Print version info and exit", ""), |
| opt(Stable, Flag, "v", "verbose", "Use verbose output", ""), |
| ]; |
| |
| // Options in this list are hidden from `rustc --help` by default, but are |
| // shown by `rustc --help -v`. |
| let verbose_only = [ |
| opt( |
| Stable, |
| Multi, |
| "", |
| "extern", |
| "Specify where an external rust library is located", |
| "<NAME>[=<PATH>]", |
| ), |
| opt(Stable, Opt, "", "sysroot", "Override the system root", "<PATH>"), |
| opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", "<FLAG>"), |
| opt( |
| Stable, |
| Opt, |
| "", |
| "error-format", |
| "How errors and other messages are produced", |
| "<human|json|short>", |
| ), |
| opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", "<CONFIG>"), |
| opt( |
| Stable, |
| Opt, |
| "", |
| "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( |
| Stable, |
| Opt, |
| "", |
| "diagnostic-width", |
| "Inform rustc of the width of the output so that diagnostics can be truncated to fit", |
| "<WIDTH>", |
| ), |
| opt( |
| Stable, |
| Multi, |
| "", |
| "remap-path-prefix", |
| "Remap source names in all output (compiler messages and output files)", |
| "<FROM>=<TO>", |
| ), |
| opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "<VAR>=<VALUE>"), |
| ]; |
| options.extend(verbose_only.into_iter().map(|mut opt| { |
| opt.is_verbose_help_only = true; |
| opt |
| })); |
| |
| options |
| } |
| |
| pub fn get_cmd_lint_options( |
| early_dcx: &EarlyDiagCtxt, |
| matches: &getopts::Matches, |
| ) -> (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, 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_dcx.early_fatal(format!("unknown lint level: `{cap}`"))) |
| }); |
| |
| (lint_opts, describe_lints, lint_cap) |
| } |
| |
| /// Parses the `--color` flag. |
| pub fn parse_color(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> ColorConfig { |
| match matches.opt_str("color").as_deref() { |
| Some("auto") => ColorConfig::Auto, |
| Some("always") => ColorConfig::Always, |
| Some("never") => ColorConfig::Never, |
| |
| None => ColorConfig::Auto, |
| |
| Some(arg) => early_dcx.early_fatal(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_color: ColorConfig, |
| json_artifact_notifications: bool, |
| /// Output start and end timestamps of several high-level compilation sections |
| /// (frontend, backend, linker). |
| json_timings: bool, |
| pub json_unused_externs: JsonUnusedExterns, |
| 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(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> JsonConfig { |
| let mut json_rendered = 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; |
| let mut json_timings = 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_dcx.early_fatal("cannot specify the `--color` option with `--json`"); |
| } |
| |
| for sub_option in option.split(',') { |
| match sub_option { |
| "diagnostic-short" => json_rendered = HumanReadableErrorType::Short, |
| "diagnostic-unicode" => { |
| json_rendered = HumanReadableErrorType::Unicode; |
| } |
| "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, |
| "artifacts" => json_artifact_notifications = true, |
| "timings" => json_timings = true, |
| "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud, |
| "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent, |
| "future-incompat" => json_future_incompat = true, |
| s => early_dcx.early_fatal(format!("unknown `--json` option `{s}`")), |
| } |
| } |
| } |
| |
| JsonConfig { |
| json_rendered, |
| json_color, |
| json_artifact_notifications, |
| json_timings, |
| json_unused_externs, |
| json_future_incompat, |
| } |
| } |
| |
| /// Parses the `--error-format` flag. |
| pub fn parse_error_format( |
| early_dcx: &mut EarlyDiagCtxt, |
| matches: &getopts::Matches, |
| color_config: ColorConfig, |
| json_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_deref() { |
| None | Some("human") => ErrorOutputType::HumanReadable { color_config, .. }, |
| Some("human-annotate-rs") => ErrorOutputType::HumanReadable { |
| kind: HumanReadableErrorType::AnnotateSnippet, |
| color_config, |
| }, |
| Some("json") => { |
| ErrorOutputType::Json { pretty: false, json_rendered, color_config: json_color } |
| } |
| Some("pretty-json") => { |
| ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color } |
| } |
| Some("short") => { |
| ErrorOutputType::HumanReadable { kind: HumanReadableErrorType::Short, color_config } |
| } |
| Some("human-unicode") => ErrorOutputType::HumanReadable { |
| kind: HumanReadableErrorType::Unicode, |
| color_config, |
| }, |
| Some(arg) => { |
| early_dcx.set_error_format(ErrorOutputType::HumanReadable { color_config, .. }); |
| early_dcx.early_fatal(format!( |
| "argument for `--error-format` must be `human`, `human-annotate-rs`, \ |
| `human-unicode`, `json`, `pretty-json` or `short` (instead was `{arg}`)" |
| )) |
| } |
| } |
| } else { |
| ErrorOutputType::HumanReadable { color_config, .. } |
| }; |
| |
| 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_dcx.early_fatal("using `--json` requires also using `--error-format=json`"); |
| } |
| |
| _ => {} |
| } |
| |
| error_format |
| } |
| |
| pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Edition { |
| let edition = match matches.opt_str("edition") { |
| Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| { |
| early_dcx.early_fatal(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 {edition}, but the latest edition supported by this Rust version is {LATEST_STABLE_EDITION}" |
| ) |
| } else { |
| format!("edition {edition} is unstable and only available with -Z unstable-options") |
| }; |
| early_dcx.early_fatal(msg) |
| } |
| |
| edition |
| } |
| |
| fn check_error_format_stability( |
| early_dcx: &EarlyDiagCtxt, |
| unstable_opts: &UnstableOptions, |
| format: ErrorOutputType, |
| ) { |
| if unstable_opts.unstable_options { |
| return; |
| } |
| let format = match format { |
| ErrorOutputType::Json { pretty: true, .. } => "pretty-json", |
| ErrorOutputType::HumanReadable { kind, .. } => match kind { |
| HumanReadableErrorType::AnnotateSnippet => "human-annotate-rs", |
| HumanReadableErrorType::Unicode => "human-unicode", |
| _ => return, |
| }, |
| _ => return, |
| }; |
| early_dcx.early_fatal(format!("`--error-format={format}` is unstable")) |
| } |
| |
| fn parse_output_types( |
| early_dcx: &EarlyDiagCtxt, |
| unstable_opts: &UnstableOptions, |
| matches: &getopts::Matches, |
| ) -> OutputTypes { |
| let mut output_types = BTreeMap::new(); |
| if !unstable_opts.parse_crate_root_only { |
| for list in matches.opt_strs("emit") { |
| for output_type in list.split(',') { |
| let (shorthand, path) = split_out_file_name(output_type); |
| let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { |
| early_dcx.early_fatal(format!( |
| "unknown emission type: `{shorthand}` - expected one of: {display}", |
| display = OutputType::shorthands_display(), |
| )) |
| }); |
| if output_type == OutputType::ThinLinkBitcode && !unstable_opts.unstable_options { |
| early_dcx.early_fatal(format!( |
| "{} requested but -Zunstable-options not specified", |
| OutputType::ThinLinkBitcode.shorthand() |
| )); |
| } |
| output_types.insert(output_type, path); |
| } |
| } |
| }; |
| if output_types.is_empty() { |
| output_types.insert(OutputType::Exe, None); |
| } |
| OutputTypes(output_types) |
| } |
| |
| fn split_out_file_name(arg: &str) -> (&str, Option<OutFileName>) { |
| match arg.split_once('=') { |
| None => (arg, None), |
| Some((kind, "-")) => (kind, Some(OutFileName::Stdout)), |
| Some((kind, path)) => (kind, Some(OutFileName::Real(PathBuf::from(path)))), |
| } |
| } |
| |
| fn should_override_cgus_and_disable_thinlto( |
| early_dcx: &EarlyDiagCtxt, |
| output_types: &OutputTypes, |
| matches: &getopts::Matches, |
| mut codegen_units: Option<usize>, |
| ) -> (bool, Option<usize>) { |
| let mut disable_local_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_dcx.early_warn(format!( |
| "`--emit={ot}` with `-o` incompatible with \ |
| `-C codegen-units=N` for N > 1", |
| )); |
| } |
| early_dcx.early_warn("resetting to default -C codegen-units=1"); |
| codegen_units = Some(1); |
| disable_local_thinlto = true; |
| } |
| } |
| _ => { |
| codegen_units = Some(1); |
| disable_local_thinlto = true; |
| } |
| } |
| } |
| |
| if codegen_units == Some(0) { |
| early_dcx.early_fatal("value for codegen units must be a positive non-zero integer"); |
| } |
| |
| (disable_local_thinlto, codegen_units) |
| } |
| |
| fn collect_print_requests( |
| early_dcx: &EarlyDiagCtxt, |
| cg: &mut CodegenOptions, |
| unstable_opts: &UnstableOptions, |
| matches: &getopts::Matches, |
| ) -> Vec<PrintRequest> { |
| let mut prints = Vec::<PrintRequest>::new(); |
| if cg.target_cpu.as_deref() == Some("help") { |
| prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout }); |
| cg.target_cpu = None; |
| }; |
| if cg.target_feature == "help" { |
| prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout }); |
| cg.target_feature = String::new(); |
| } |
| |
| // We disallow reusing the same path in multiple prints, such as `--print |
| // cfg=output.txt --print link-args=output.txt`, because outputs are printed |
| // by disparate pieces of the compiler, and keeping track of which files |
| // need to be overwritten vs appended to is annoying. |
| let mut printed_paths = FxHashSet::default(); |
| |
| prints.extend(matches.opt_strs("print").into_iter().map(|req| { |
| let (req, out) = split_out_file_name(&req); |
| |
| let kind = if let Some((print_name, print_kind)) = |
| PRINT_KINDS.iter().find(|&&(name, _)| name == req) |
| { |
| check_print_request_stability(early_dcx, unstable_opts, (print_name, *print_kind)); |
| *print_kind |
| } else { |
| let is_nightly = nightly_options::match_is_nightly_build(matches); |
| emit_unknown_print_request_help(early_dcx, req, is_nightly) |
| }; |
| |
| let out = out.unwrap_or(OutFileName::Stdout); |
| if let OutFileName::Real(path) = &out { |
| if !printed_paths.insert(path.clone()) { |
| early_dcx.early_fatal(format!( |
| "cannot print multiple outputs to the same path: {}", |
| path.display(), |
| )); |
| } |
| } |
| |
| PrintRequest { kind, out } |
| })); |
| |
| prints |
| } |
| |
| fn check_print_request_stability( |
| early_dcx: &EarlyDiagCtxt, |
| unstable_opts: &UnstableOptions, |
| (print_name, print_kind): (&str, PrintKind), |
| ) { |
| if !is_print_request_stable(print_kind) && !unstable_opts.unstable_options { |
| early_dcx.early_fatal(format!( |
| "the `-Z unstable-options` flag must also be passed to enable the `{print_name}` \ |
| print option" |
| )); |
| } |
| } |
| |
| fn is_print_request_stable(print_kind: PrintKind) -> bool { |
| match print_kind { |
| PrintKind::AllTargetSpecsJson |
| | PrintKind::CheckCfg |
| | PrintKind::CrateRootLintLevels |
| | PrintKind::SupportedCrateTypes |
| | PrintKind::TargetSpecJson => false, |
| _ => true, |
| } |
| } |
| |
| fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! { |
| let prints = PRINT_KINDS |
| .iter() |
| .filter_map(|(name, kind)| { |
| // If we're not on nightly, we don't want to print unstable options |
| if !is_nightly && !is_print_request_stable(*kind) { |
| None |
| } else { |
| Some(format!("`{name}`")) |
| } |
| }) |
| .collect::<Vec<_>>(); |
| let prints = prints.join(", "); |
| |
| let mut diag = early_dcx.early_struct_fatal(format!("unknown print request: `{req}`")); |
| #[allow(rustc::diagnostic_outside_of_impl)] |
| diag.help(format!("valid print requests are: {prints}")); |
| |
| if req == "lints" { |
| diag.help(format!("use `-Whelp` to print a list of lints")); |
| } |
| |
| diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information")); |
| diag.emit() |
| } |
| |
| pub fn parse_target_triple(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> TargetTuple { |
| match matches.opt_str("target") { |
| Some(target) if target.ends_with(".json") => { |
| let path = Path::new(&target); |
| TargetTuple::from_path(path).unwrap_or_else(|_| { |
| early_dcx.early_fatal(format!("target file {path:?} does not exist")) |
| }) |
| } |
| Some(target) => TargetTuple::TargetTuple(target), |
| _ => TargetTuple::from_tuple(host_tuple()), |
| } |
| } |
| |
| fn parse_opt_level( |
| early_dcx: &EarlyDiagCtxt, |
| matches: &getopts::Matches, |
| cg: &CodegenOptions, |
| ) -> 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.split('=').next() { Some(i) } else { None } |
| }) |
| .max(); |
| if max_o > max_c { |
| OptLevel::Aggressive |
| } else { |
| match cg.opt_level.as_ref() { |
| "0" => OptLevel::No, |
| "1" => OptLevel::Less, |
| "2" => OptLevel::More, |
| "3" => OptLevel::Aggressive, |
| "s" => OptLevel::Size, |
| "z" => OptLevel::SizeMin, |
| arg => { |
| early_dcx.early_fatal(format!( |
| "optimization level needs to be \ |
| between 0-3, s or z (instead was `{arg}`)" |
| )); |
| } |
| } |
| } |
| } |
| |
| fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> 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.split('=').next() { Some(i) } else { None } |
| }) |
| .max(); |
| if max_g > max_c { DebugInfo::Full } else { cg.debuginfo } |
| } |
| |
| fn parse_assert_incr_state( |
| early_dcx: &EarlyDiagCtxt, |
| opt_assertion: &Option<String>, |
| ) -> 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_dcx.early_fatal(format!("unexpected incremental state assertion value: {s}")) |
| } |
| None => None, |
| } |
| } |
| |
| pub fn parse_externs( |
| early_dcx: &EarlyDiagCtxt, |
| matches: &getopts::Matches, |
| unstable_opts: &UnstableOptions, |
| ) -> 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 ExternOpt { crate_name: name, path, options } = |
| split_extern_opt(early_dcx, unstable_opts, &arg).unwrap_or_else(|e| e.emit()); |
| |
| 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 |
| let path = CanonicalizedPath::new(path); |
| 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; |
| let mut force = false; |
| if let Some(opts) = options { |
| if !is_unstable_enabled { |
| early_dcx.early_fatal( |
| "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_dcx.early_fatal( |
| "the `noprelude` --extern option requires a file path", |
| ); |
| } |
| } |
| "nounused" => nounused_dep = true, |
| "force" => force = true, |
| _ => early_dcx.early_fatal(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; |
| // and `force` |
| entry.force |= force; |
| // If any flag is missing `noprelude`, then add to the prelude. |
| entry.add_prelude |= add_prelude; |
| } |
| Externs(externs) |
| } |
| |
| fn parse_remap_path_prefix( |
| early_dcx: &EarlyDiagCtxt, |
| matches: &getopts::Matches, |
| unstable_opts: &UnstableOptions, |
| ) -> 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_dcx.early_fatal("--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 |
| } |
| |
| fn parse_logical_env( |
| early_dcx: &EarlyDiagCtxt, |
| matches: &getopts::Matches, |
| ) -> FxIndexMap<String, String> { |
| let mut vars = FxIndexMap::default(); |
| |
| for arg in matches.opt_strs("env-set") { |
| if let Some((name, val)) = arg.split_once('=') { |
| vars.insert(name.to_string(), val.to_string()); |
| } else { |
| early_dcx.early_fatal(format!("`--env-set`: specify value for variable `{arg}`")); |
| } |
| } |
| |
| vars |
| } |
| |
| // JUSTIFICATION: before wrapper fn is available |
| #[allow(rustc::bad_opt_access)] |
| pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches) -> Options { |
| let color = parse_color(early_dcx, matches); |
| |
| let edition = parse_crate_edition(early_dcx, matches); |
| |
| let JsonConfig { |
| json_rendered, |
| json_color, |
| json_artifact_notifications, |
| json_timings, |
| json_unused_externs, |
| json_future_incompat, |
| } = parse_json(early_dcx, matches); |
| |
| let error_format = parse_error_format(early_dcx, matches, color, json_color, json_rendered); |
| |
| early_dcx.set_error_format(error_format); |
| |
| let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| { |
| early_dcx.early_fatal("`--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_dcx.early_fatal(e)); |
| |
| let mut target_modifiers = BTreeMap::<OptionsTargetModifiers, String>::new(); |
| |
| let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers); |
| let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches); |
| |
| if !unstable_opts.unstable_options && json_timings { |
| early_dcx.early_fatal("--json=timings is unstable and requires using `-Zunstable-options`"); |
| } |
| |
| check_error_format_stability(early_dcx, &unstable_opts, error_format); |
| |
| let output_types = parse_output_types(early_dcx, &unstable_opts, matches); |
| |
| let mut cg = CodegenOptions::build(early_dcx, matches, &mut target_modifiers); |
| let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto( |
| early_dcx, |
| &output_types, |
| matches, |
| cg.codegen_units, |
| ); |
| |
| if unstable_opts.threads == 0 { |
| early_dcx.early_fatal("value for threads must be a positive non-zero integer"); |
| } |
| |
| if unstable_opts.threads == parse::MAX_THREADS_CAP { |
| early_dcx.early_warn(format!("number of threads was capped at {}", parse::MAX_THREADS_CAP)); |
| } |
| |
| let incremental = cg.incremental.as_ref().map(PathBuf::from); |
| |
| let assert_incr_state = parse_assert_incr_state(early_dcx, &unstable_opts.assert_incr_state); |
| |
| if cg.profile_generate.enabled() && cg.profile_use.is_some() { |
| early_dcx.early_fatal("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_dcx.early_fatal( |
| "option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`", |
| ); |
| } |
| |
| // Check for unstable values of `-C symbol-mangling-version`. |
| // This is what prevents them from being used on stable compilers. |
| match cg.symbol_mangling_version { |
| // Stable values: |
| None | Some(SymbolManglingVersion::V0) => {} |
| |
| // Unstable values: |
| Some(SymbolManglingVersion::Legacy) => { |
| if !unstable_opts.unstable_options { |
| early_dcx.early_fatal( |
| "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`", |
| ); |
| } |
| } |
| Some(SymbolManglingVersion::Hashed) => { |
| if !unstable_opts.unstable_options { |
| early_dcx.early_fatal( |
| "`-C symbol-mangling-version=hashed` requires `-Z unstable-options`", |
| ); |
| } |
| } |
| } |
| |
| if cg.instrument_coverage != InstrumentCoverage::No { |
| if cg.profile_generate.enabled() || cg.profile_use.is_some() { |
| early_dcx.early_fatal( |
| "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_dcx.early_warn( |
| "-C instrument-coverage requires symbol mangling version `v0`, \ |
| but `-C symbol-mangling-version=legacy` was specified", |
| ); |
| } |
| Some(SymbolManglingVersion::V0) => {} |
| Some(SymbolManglingVersion::Hashed) => { |
| early_dcx.early_warn( |
| "-C instrument-coverage requires symbol mangling version `v0`, \ |
| but `-C symbol-mangling-version=hashed` was specified", |
| ); |
| } |
| } |
| } |
| |
| if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") { |
| // FIXME: this is only mutation of UnstableOptions here, move into |
| // UnstableOptions::build? |
| 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_dcx.early_fatal("options `-C embed-bitcode=no` and `-C lto` are incompatible") |
| } |
| } |
| } |
| |
| let unstable_options_enabled = nightly_options::is_unstable_enabled(matches); |
| if !unstable_options_enabled && cg.force_frame_pointers == FramePointer::NonLeaf { |
| early_dcx.early_fatal( |
| "`-Cforce-frame-pointers=non-leaf` or `always` also requires `-Zunstable-options` \ |
| and a nightly compiler", |
| ) |
| } |
| |
| if !nightly_options::is_unstable_enabled(matches) |
| && unstable_opts.offload.contains(&Offload::Enable) |
| { |
| early_dcx.early_fatal( |
| "`-Zoffload=Enable` also requires `-Zunstable-options` \ |
| and a nightly compiler", |
| ) |
| } |
| |
| let target_triple = parse_target_triple(early_dcx, matches); |
| |
| // Ensure `-Z unstable-options` is required when using the unstable `-C link-self-contained` and |
| // `-C linker-flavor` options. |
| if !unstable_options_enabled { |
| if let Err(error) = cg.link_self_contained.check_unstable_variants(&target_triple) { |
| early_dcx.early_fatal(error); |
| } |
| |
| if let Some(flavor) = cg.linker_flavor { |
| if flavor.is_unstable() { |
| early_dcx.early_fatal(format!( |
| "the linker flavor `{}` is unstable, the `-Z unstable-options` \ |
| flag must also be passed to use the unstable values", |
| flavor.desc() |
| )); |
| } |
| } |
| } |
| |
| // Check `-C link-self-contained` for consistency: individual components cannot be both enabled |
| // and disabled at the same time. |
| if let Some(erroneous_components) = cg.link_self_contained.check_consistency() { |
| let names: String = erroneous_components |
| .into_iter() |
| .map(|c| c.as_str().unwrap()) |
| .intersperse(", ") |
| .collect(); |
| early_dcx.early_fatal(format!( |
| "some `-C link-self-contained` components were both enabled and disabled: {names}" |
| )); |
| } |
| |
| let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); |
| |
| // -Zretpoline-external-thunk also requires -Zretpoline |
| if unstable_opts.retpoline_external_thunk { |
| unstable_opts.retpoline = true; |
| target_modifiers.insert( |
| OptionsTargetModifiers::UnstableOptions(UnstableOptionsTargetModifiers::retpoline), |
| "true".to_string(), |
| ); |
| } |
| |
| let cg = cg; |
| |
| let opt_level = parse_opt_level(early_dcx, matches, &cg); |
| // 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); |
| let debuginfo_compression = unstable_opts.debuginfo_compression; |
| |
| if !unstable_options_enabled { |
| if let Err(error) = cg.linker_features.check_unstable_variants(&target_triple) { |
| early_dcx.early_fatal(error); |
| } |
| } |
| |
| let crate_name = matches.opt_str("crate-name"); |
| let unstable_features = UnstableFeatures::from_environment(crate_name.as_deref()); |
| // Parse any `-l` flags, which link to native libraries. |
| let libs = parse_native_libs(early_dcx, &unstable_opts, unstable_features, matches); |
| |
| let test = matches.opt_present("test"); |
| |
| if !cg.remark.is_empty() && debuginfo == DebugInfo::None { |
| early_dcx.early_warn("-C remark requires \"-C debuginfo=n\" to show source locations"); |
| } |
| |
| if cg.remark.is_empty() && unstable_opts.remark_dir.is_some() { |
| early_dcx |
| .early_warn("using -Z remark-dir without enabling remarks using e.g. -C remark=all"); |
| } |
| |
| let externs = parse_externs(early_dcx, matches, &unstable_opts); |
| |
| let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts); |
| |
| let pretty = parse_pretty(early_dcx, &unstable_opts); |
| |
| // query-dep-graph is required if dump-dep-graph is given #106736 |
| if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph { |
| early_dcx.early_fatal("can't dump dependency graph without `-Z query-dep-graph`"); |
| } |
| |
| let logical_env = parse_logical_env(early_dcx, matches); |
| |
| let sysroot = Sysroot::new(matches.opt_str("sysroot").map(PathBuf::from)); |
| |
| let real_source_base_dir = |suffix: &str, confirm: &str| { |
| let mut candidate = sysroot.path().join(suffix); |
| if let Ok(metadata) = candidate.symlink_metadata() { |
| // Replace the symlink bootstrap 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. |
| candidate.join(confirm).is_file().then_some(candidate) |
| }; |
| |
| let real_rust_source_base_dir = |
| // This is the location used by the `rust-src` `rustup` component. |
| real_source_base_dir("lib/rustlib/src/rust", "library/std/src/lib.rs"); |
| |
| let real_rustc_dev_source_base_dir = |
| // This is the location used by the `rustc-dev` `rustup` component. |
| real_source_base_dir("lib/rustlib/rustc-src/rust", "compiler/rustc/src/main.rs"); |
| |
| let mut search_paths = vec![]; |
| for s in &matches.opt_strs("L") { |
| search_paths.push(SearchPath::from_cli_opt( |
| sysroot.path(), |
| &target_triple, |
| early_dcx, |
| s, |
| unstable_opts.unstable_options, |
| )); |
| } |
| |
| let working_dir = std::env::current_dir().unwrap_or_else(|e| { |
| early_dcx.early_fatal(format!("Current directory is invalid: {e}")); |
| }); |
| |
| let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts); |
| let working_dir = file_mapping.to_real_filename(&working_dir); |
| |
| let verbose = matches.opt_present("verbose") || unstable_opts.verbose_internals; |
| |
| Options { |
| assert_incr_state, |
| crate_types, |
| optimize: opt_level, |
| debuginfo, |
| debuginfo_compression, |
| lint_opts, |
| lint_cap, |
| describe_lints, |
| output_types, |
| search_paths, |
| sysroot, |
| target_triple, |
| test, |
| incremental, |
| untracked_state_hash: Default::default(), |
| unstable_opts, |
| prints, |
| cg, |
| error_format, |
| diagnostic_width, |
| externs, |
| unstable_features, |
| crate_name, |
| libs, |
| debug_assertions, |
| actually_rustdoc: false, |
| resolve_doc_links: ResolveDocLinks::ExportedMetadata, |
| trimmed_def_paths: false, |
| cli_forced_codegen_units: codegen_units, |
| cli_forced_local_thinlto_off: disable_local_thinlto, |
| remap_path_prefix, |
| real_rust_source_base_dir, |
| real_rustc_dev_source_base_dir, |
| edition, |
| json_artifact_notifications, |
| json_timings, |
| json_unused_externs, |
| json_future_incompat, |
| pretty, |
| working_dir, |
| color, |
| logical_env, |
| verbose, |
| target_modifiers, |
| } |
| } |
| |
| fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> 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, |
| "ast-tree,expanded" => AstTreeExpanded, |
| "hir" => Hir(PpHirMode::Normal), |
| "hir,identified" => Hir(PpHirMode::Identified), |
| "hir,typed" => Hir(PpHirMode::Typed), |
| "hir-tree" => HirTree, |
| "thir-tree" => ThirTree, |
| "thir-flat" => ThirFlat, |
| "mir" => Mir, |
| "stable-mir" => StableMir, |
| "mir-cfg" => MirCFG, |
| name => early_dcx.early_fatal(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`, `thir-flat`, `mir`, `stable-mir`, or \ |
| `mir-cfg`; got {name}" |
| )), |
| }; |
| debug!("got unpretty option: {first:?}"); |
| Some(first) |
| } |
| |
| pub fn make_crate_type_option() -> RustcOptGroup { |
| make_opt( |
| OptionStability::Stable, |
| OptionKind::Multi, |
| "", |
| "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, |
| "sdylib" => CrateType::Sdylib, |
| _ => { |
| return Err(format!( |
| "unknown crate type: `{part}`, expected one of: \ |
| `lib`, `rlib`, `staticlib`, `dylib`, `cdylib`, `bin`, `proc-macro`", |
| )); |
| } |
| }; |
| if !crate_types.contains(&new_part) { |
| crate_types.push(new_part) |
| } |
| } |
| } |
| |
| Ok(crate_types) |
| } |
| |
| pub mod nightly_options { |
| use rustc_feature::UnstableFeatures; |
| |
| use super::{OptionStability, RustcOptGroup}; |
| use crate::EarlyDiagCtxt; |
| |
| 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()) |
| } |
| |
| fn is_nightly_build(krate: Option<&str>) -> bool { |
| UnstableFeatures::from_environment(krate).is_nightly_build() |
| } |
| |
| pub fn check_nightly_options( |
| early_dcx: &EarlyDiagCtxt, |
| 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); |
| let mut nightly_options_on_stable = 0; |
| |
| 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_dcx.early_fatal(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 => { |
| nightly_options_on_stable += 1; |
| let msg = format!( |
| "the option `{}` is only accepted on the nightly compiler", |
| opt.name |
| ); |
| // The non-zero nightly_options_on_stable will force an early_fatal eventually. |
| let _ = early_dcx.early_err(msg); |
| } |
| OptionStability::Stable => {} |
| } |
| } |
| if nightly_options_on_stable > 0 { |
| early_dcx |
| .early_help("consider switching to a nightly toolchain: `rustup default nightly`"); |
| early_dcx.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>"); |
| early_dcx.early_note("for more information about Rust's stability policy, see <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features>"); |
| early_dcx.early_fatal(format!( |
| "{} nightly option{} were parsed", |
| nightly_options_on_stable, |
| if nightly_options_on_stable > 1 { "s" } else { "" } |
| )); |
| } |
| } |
| } |
| |
| 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), |
| CrateType::Sdylib => "sdylib".fmt(f), |
| } |
| } |
| } |
| |
| impl IntoDiagArg for CrateType { |
| fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue { |
| self.to_string().into_diag_arg(&mut None) |
| } |
| } |
| |
| #[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 PpHirMode { |
| /// `-Zunpretty=hir` |
| Normal, |
| /// `-Zunpretty=hir,identified` |
| Identified, |
| /// `-Zunpretty=hir,typed` |
| Typed, |
| } |
| |
| #[derive(Copy, Clone, PartialEq, Debug)] |
| /// Pretty print mode |
| pub enum PpMode { |
| /// Options that print the source code, i.e. |
| /// `-Zunpretty=normal` and `-Zunpretty=expanded` |
| Source(PpSourceMode), |
| /// `-Zunpretty=ast-tree` |
| AstTree, |
| /// `-Zunpretty=ast-tree,expanded` |
| AstTreeExpanded, |
| /// Options that print the HIR, i.e. `-Zunpretty=hir` |
| Hir(PpHirMode), |
| /// `-Zunpretty=hir-tree` |
| HirTree, |
| /// `-Zunpretty=thir-tree` |
| ThirTree, |
| /// `-Zunpretty=thir-flat` |
| ThirFlat, |
| /// `-Zunpretty=mir` |
| Mir, |
| /// `-Zunpretty=mir-cfg` |
| MirCFG, |
| /// `-Zunpretty=stable-mir` |
| StableMir, |
| } |
| |
| impl PpMode { |
| pub fn needs_ast_map(&self) -> bool { |
| use PpMode::*; |
| use PpSourceMode::*; |
| match *self { |
| Source(Normal | Identified) | AstTree => false, |
| |
| Source(Expanded | ExpandedIdentified | ExpandedHygiene) |
| | AstTreeExpanded |
| | Hir(_) |
| | HirTree |
| | ThirTree |
| | ThirFlat |
| | Mir |
| | MirCFG |
| | StableMir => true, |
| } |
| } |
| |
| pub fn needs_analysis(&self) -> bool { |
| use PpMode::*; |
| matches!(*self, Hir(PpHirMode::Typed) | Mir | StableMir | MirCFG | ThirTree | ThirFlat) |
| } |
| } |
| |
| #[derive(Clone, Hash, PartialEq, Eq, Debug)] |
| pub enum WasiExecModel { |
| Command, |
| Reactor, |
| } |
| |
| /// 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 std::collections::BTreeMap; |
| use std::hash::Hash; |
| use std::num::NonZero; |
| use std::path::PathBuf; |
| |
| use rustc_abi::Align; |
| use rustc_data_structures::fx::FxIndexMap; |
| use rustc_data_structures::stable_hasher::StableHasher; |
| use rustc_errors::LanguageIdentifier; |
| use rustc_feature::UnstableFeatures; |
| use rustc_hashes::Hash64; |
| use rustc_span::RealFileName; |
| use rustc_span::edition::Edition; |
| use rustc_target::spec::{ |
| CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, |
| RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTuple, |
| TlsModel, |
| }; |
| |
| use super::{ |
| AutoDiff, BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions, |
| CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, |
| InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, |
| LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OomStrategy, OptLevel, OutFileName, |
| OutputType, OutputTypes, PatchableFunctionEntry, Polonius, RemapPathScopeComponents, |
| ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, |
| SymbolManglingVersion, WasiExecModel, |
| }; |
| use crate::lint; |
| use crate::utils::NativeLib; |
| |
| pub(crate) trait DepTrackingHash { |
| fn hash( |
| &self, |
| hasher: &mut StableHasher, |
| 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 StableHasher, _: ErrorOutputType, _for_crate_hash: bool) { |
| Hash::hash(self, hasher); |
| } |
| } |
| )+}; |
| } |
| |
| impl<T: DepTrackingHash> DepTrackingHash for Option<T> { |
| fn hash( |
| &self, |
| hasher: &mut StableHasher, |
| 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!( |
| (), |
| AutoDiff, |
| Offload, |
| bool, |
| usize, |
| NonZero<usize>, |
| u64, |
| Hash64, |
| String, |
| PathBuf, |
| lint::Level, |
| WasiExecModel, |
| u32, |
| FramePointer, |
| RelocModel, |
| CodeModel, |
| TlsModel, |
| InstrumentCoverage, |
| CoverageOptions, |
| InstrumentXRay, |
| CrateType, |
| MergeFunctions, |
| OnBrokenPipe, |
| PanicStrategy, |
| RelroLevel, |
| OptLevel, |
| LtoCli, |
| DebugInfo, |
| DebugInfoCompression, |
| MirStripDebugInfo, |
| CollapseMacroDebuginfo, |
| UnstableFeatures, |
| NativeLib, |
| SanitizerSet, |
| CFGuard, |
| CFProtection, |
| TargetTuple, |
| Edition, |
| LinkerPluginLto, |
| ResolveDocLinks, |
| SplitDebuginfo, |
| SplitDwarfKind, |
| StackProtector, |
| SwitchWithOptPath, |
| SymbolManglingVersion, |
| SymbolVisibility, |
| RemapPathScopeComponents, |
| SourceFileHashAlgorithm, |
| OutFileName, |
| OutputType, |
| RealFileName, |
| LocationDetail, |
| FmtDebug, |
| BranchProtection, |
| OomStrategy, |
| LanguageIdentifier, |
| NextSolverConfig, |
| PatchableFunctionEntry, |
| Polonius, |
| InliningThreshold, |
| FunctionReturn, |
| Align, |
| ); |
| |
| impl<T1, T2> DepTrackingHash for (T1, T2) |
| where |
| T1: DepTrackingHash, |
| T2: DepTrackingHash, |
| { |
| fn hash( |
| &self, |
| hasher: &mut StableHasher, |
| 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 StableHasher, |
| 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 StableHasher, |
| 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<T: DepTrackingHash, V: DepTrackingHash> DepTrackingHash for FxIndexMap<T, V> { |
| fn hash( |
| &self, |
| hasher: &mut StableHasher, |
| error_format: ErrorOutputType, |
| for_crate_hash: bool, |
| ) { |
| Hash::hash(&self.len(), hasher); |
| for (key, value) in self.iter() { |
| DepTrackingHash::hash(key, hasher, error_format, for_crate_hash); |
| DepTrackingHash::hash(value, hasher, error_format, for_crate_hash); |
| } |
| } |
| } |
| |
| impl DepTrackingHash for OutputTypes { |
| fn hash( |
| &self, |
| hasher: &mut StableHasher, |
| 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 StableHasher, |
| 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_v2"; |
| |
| 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, |
| } |
| |
| /// How to perform collapse macros debug info |
| /// if-ext - if macro from different crate (related to callsite code) |
| /// | cmd \ attr | no | (unspecified) | external | yes | |
| /// | no | no | no | no | no | |
| /// | (unspecified) | no | no | if-ext | yes | |
| /// | external | no | if-ext | if-ext | yes | |
| /// | yes | yes | yes | yes | yes | |
| #[derive(Clone, Copy, PartialEq, Hash, Debug)] |
| pub enum CollapseMacroDebuginfo { |
| /// Don't collapse debuginfo for the macro |
| No = 0, |
| /// Unspecified value |
| Unspecified = 1, |
| /// Collapse debuginfo if the macro comes from a different crate |
| External = 2, |
| /// Collapse debuginfo for the macro |
| Yes = 3, |
| } |
| |
| /// Which format to use for `-Z dump-mono-stats` |
| #[derive(Clone, Copy, PartialEq, Hash, Debug)] |
| pub enum DumpMonoStatsFormat { |
| /// Pretty-print a markdown table |
| Markdown, |
| /// Emit structured JSON |
| Json, |
| } |
| |
| impl DumpMonoStatsFormat { |
| pub fn extension(self) -> &'static str { |
| match self { |
| Self::Markdown => "md", |
| Self::Json => "json", |
| } |
| } |
| } |
| |
| /// `-Z patchable-function-entry` representation - how many nops to put before and after function |
| /// entry. |
| #[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] |
| pub struct PatchableFunctionEntry { |
| /// Nops before the entry |
| prefix: u8, |
| /// Nops after the entry |
| entry: u8, |
| } |
| |
| impl PatchableFunctionEntry { |
| pub fn from_total_and_prefix_nops( |
| total_nops: u8, |
| prefix_nops: u8, |
| ) -> Option<PatchableFunctionEntry> { |
| if total_nops < prefix_nops { |
| None |
| } else { |
| Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops }) |
| } |
| } |
| pub fn prefix(&self) -> u8 { |
| self.prefix |
| } |
| pub fn entry(&self) -> u8 { |
| self.entry |
| } |
| } |
| |
| /// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy, |
| /// or future prototype. |
| #[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] |
| pub enum Polonius { |
| /// The default value: disabled. |
| #[default] |
| Off, |
| |
| /// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`. |
| Legacy, |
| |
| /// In-tree prototype, extending the NLL infrastructure. |
| Next, |
| } |
| |
| impl Polonius { |
| /// Returns whether the legacy version of polonius is enabled |
| pub fn is_legacy_enabled(&self) -> bool { |
| matches!(self, Polonius::Legacy) |
| } |
| |
| /// Returns whether the "next" version of polonius is enabled |
| pub fn is_next_enabled(&self) -> bool { |
| matches!(self, Polonius::Next) |
| } |
| } |
| |
| #[derive(Clone, Copy, PartialEq, Hash, Debug)] |
| pub enum InliningThreshold { |
| Always, |
| Sometimes(usize), |
| Never, |
| } |
| |
| impl Default for InliningThreshold { |
| fn default() -> Self { |
| Self::Sometimes(100) |
| } |
| } |
| |
| /// The different settings that the `-Zfunction-return` flag can have. |
| #[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] |
| pub enum FunctionReturn { |
| /// Keep the function return unmodified. |
| #[default] |
| Keep, |
| |
| /// Replace returns with jumps to thunk, without emitting the thunk. |
| ThunkExtern, |
| } |
| |
| /// Whether extra span comments are included when dumping MIR, via the `-Z mir-include-spans` flag. |
| /// By default, only enabled in the NLL MIR dumps, and disabled in all other passes. |
| #[derive(Clone, Copy, Default, PartialEq, Debug)] |
| pub enum MirIncludeSpans { |
| Off, |
| On, |
| /// Default: include extra comments in NLL MIR dumps only. Can be ignored and considered as |
| /// `Off` in all other cases. |
| #[default] |
| Nll, |
| } |
| |
| impl MirIncludeSpans { |
| /// Unless opting into extra comments for all passes, they can be considered disabled. |
| /// The cases where a distinction between on/off and a per-pass value can exist will be handled |
| /// in the passes themselves: i.e. the `Nll` value is considered off for all intents and |
| /// purposes, except for the NLL MIR dump pass. |
| pub fn is_enabled(self) -> bool { |
| self == MirIncludeSpans::On |
| } |
| } |