#![allow(rustc::untranslatable_diagnostic)] 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 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, RealFileName, SourceFileHashAlgorithm, Symbol, sym,
};
use rustc_target::spec::{
FramePointer, LinkSelfContainedComponents, LinkerFeatures, SplitDebuginfo, Target, TargetTuple,
};
use tracing::debug;
use crate::errors::FileWriteFail;
pub use crate::options::*;
use crate::search_paths::SearchPath;
use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
use crate::{EarlyDiagCtxt, HashStableContext, Session, filesearch, lint};
mod cfg;
pub mod sigpipe;
pub use cfg::{Cfg, CheckCfg, ExpectedValues};
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum Strip {
None,
Debuginfo,
Symbols,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum CFGuard {
Disabled,
NoChecks,
Checks,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum CFProtection {
None,
Branch,
Return,
Full,
}
#[derive(Clone, Copy, Debug, PartialEq, Hash, HashStable_Generic)]
pub enum OptLevel {
No, Less, Default, Aggressive, Size, SizeMin, }
#[derive(Clone, PartialEq)]
pub enum Lto {
No,
Thin,
ThinLocal,
Fat,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum LtoCli {
No,
Yes,
NoParam,
Thin,
Fat,
Unspecified,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum InstrumentCoverage {
No,
Yes,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct CoverageOptions {
pub level: CoverageLevel,
pub no_mir_spans: bool,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum CoverageLevel {
Block,
Branch,
Condition,
Mcdc,
}
impl Default for CoverageLevel {
fn default() -> Self {
Self::Block
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct InstrumentXRay {
pub always: bool,
pub never: bool,
pub ignore_loops: bool,
pub instruction_threshold: Option<usize>,
pub skip_entry: bool,
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,
}
}
}
#[derive(Default, Clone, PartialEq, Debug)]
pub struct LinkSelfContained {
pub explicitly_set: Option<bool>,
enabled_components: LinkSelfContainedComponents,
disabled_components: LinkSelfContainedComponents,
}
impl LinkSelfContained {
pub(crate) fn handle_cli_component(&mut self, component: &str) -> Option<()> {
if let Some(component_to_enable) = component.strip_prefix('+') {
self.explicitly_set = None;
self.enabled_components
.insert(LinkSelfContainedComponents::from_str(component_to_enable)?);
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)?);
Some(())
} else {
None
}
}
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();
}
}
pub fn on() -> Self {
let mut on = LinkSelfContained::default();
on.set_all_explicitly(true);
on
}
fn are_unstable_variants_set(&self) -> bool {
let any_component_set =
!self.enabled_components.is_empty() || !self.disabled_components.is_empty();
self.explicitly_set.is_none() && any_component_set
}
pub fn is_linker_enabled(&self) -> bool {
self.enabled_components.contains(LinkSelfContainedComponents::LINKER)
}
pub fn is_linker_disabled(&self) -> bool {
self.disabled_components.contains(LinkSelfContainedComponents::LINKER)
}
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) }
}
}
}
#[derive(Default, Copy, Clone, PartialEq, Debug)]
pub struct LinkerFeaturesCli {
pub enabled: LinkerFeatures,
pub disabled: LinkerFeatures,
}
impl LinkerFeaturesCli {
pub(crate) fn handle_cli_feature(&mut self, feature: &str) -> Option<()> {
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,
}
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum IncrementalStateAssertion {
Loaded,
NotLoaded,
}
#[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 }
}
}
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
pub enum FmtDebug {
Full,
Shallow,
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 SplitDwarfKind {
Single,
Split,
}
impl FromStr for SplitDwarfKind {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
Ok(match s {
"single" => SplitDwarfKind::Single,
"split" => SplitDwarfKind::Split,
_ => return Err(()),
})
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)]
#[derive(Encodable, Decodable)]
pub enum OutputType {
Bitcode,
ThinLinkBitcode,
Assembly,
LlvmAssembly,
Mir,
Metadata,
Object,
Exe,
DepInfo,
}
impl StableOrd for OutputType {
const CAN_USE_UNSTABLE_SORT: bool = true;
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 {
fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
match *self {
OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true,
OutputType::Bitcode
| OutputType::ThinLinkBitcode
| OutputType::Assembly
| OutputType::LlvmAssembly
| OutputType::Mir
| OutputType::Object => false,
}
}
pub fn shorthand(&self) -> &'static str {
match *self {
OutputType::Bitcode => "llvm-bc",
OutputType::ThinLinkBitcode => "thin-link-bitcode",
OutputType::Assembly => "asm",
OutputType::LlvmAssembly => "llvm-ir",
OutputType::Mir => "mir",
OutputType::Object => "obj",
OutputType::Metadata => "metadata",
OutputType::Exe => "link",
OutputType::DepInfo => "dep-info",
}
}
fn from_shorthand(shorthand: &str) -> Option<Self> {
Some(match shorthand {
"asm" => OutputType::Assembly,
"llvm-ir" => OutputType::LlvmAssembly,
"mir" => OutputType::Mir,
"llvm-bc" => OutputType::Bitcode,
"thin-link-bitcode" => OutputType::ThinLinkBitcode,
"obj" => OutputType::Object,
"metadata" => OutputType::Metadata,
"link" => OutputType::Exe,
"dep-info" => OutputType::DepInfo,
_ => return None,
})
}
fn shorthands_display() -> String {
format!(
"`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`",
OutputType::Bitcode.shorthand(),
OutputType::ThinLinkBitcode.shorthand(),
OutputType::Assembly.shorthand(),
OutputType::LlvmAssembly.shorthand(),
OutputType::Mir.shorthand(),
OutputType::Object.shorthand(),
OutputType::Metadata.shorthand(),
OutputType::Exe.shorthand(),
OutputType::DepInfo.shorthand(),
)
}
pub fn extension(&self) -> &'static str {
match *self {
OutputType::Bitcode => "bc",
OutputType::ThinLinkBitcode => "indexing.o",
OutputType::Assembly => "s",
OutputType::LlvmAssembly => "ll",
OutputType::Mir => "mir",
OutputType::Object => "o",
OutputType::Metadata => "rmeta",
OutputType::DepInfo => "d",
OutputType::Exe => "",
}
}
pub fn is_text_output(&self) -> bool {
match *self {
OutputType::Assembly
| OutputType::LlvmAssembly
| OutputType::Mir
| OutputType::DepInfo => true,
OutputType::Bitcode
| OutputType::ThinLinkBitcode
| OutputType::Object
| OutputType::Metadata
| OutputType::Exe => false,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorOutputType {
HumanReadable(HumanReadableErrorType, ColorConfig),
Json {
pretty: bool,
json_rendered: HumanReadableErrorType,
color_config: ColorConfig,
},
}
impl Default for ErrorOutputType {
fn default() -> Self {
Self::HumanReadable(HumanReadableErrorType::Default, ColorConfig::Auto)
}
}
#[derive(Clone, Hash, Debug)]
pub enum ResolveDocLinks {
None,
ExportedMetadata,
Exported,
All,
}
#[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)
}
pub fn contains_explicit_name(&self, key: &OutputType) -> bool {
self.0.get(key).map_or(false, |f| f.is_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()
}
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,
})
}
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,
})
}
}
#[derive(Clone)]
pub struct Externs(BTreeMap<String, ExternEntry>);
#[derive(Clone, Debug)]
pub struct ExternEntry {
pub location: ExternLocation,
pub is_private_dep: bool,
pub add_prelude: bool,
pub nounused_dep: bool,
pub force: bool,
}
#[derive(Clone, Debug)]
pub enum ExternLocation {
FoundInLibrarySearchDirectories,
ExactPaths(BTreeSet<CanonicalizedPath>),
}
impl Externs {
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 {
FileNames,
HostTuple,
Sysroot,
TargetLibdir,
CrateName,
Cfg,
CheckCfg,
CallingConventions,
TargetList,
TargetCPUs,
TargetFeatures,
RelocationModels,
CodeModels,
TlsModels,
TargetSpec,
AllTargetSpecs,
NativeStaticLibs,
StackProtectorStrategies,
LinkArgs,
SplitDebuginfo,
DeploymentTarget,
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct NextSolverConfig {
pub coherence: bool,
pub globally: bool,
}
impl Default for NextSolverConfig {
fn default() -> Self {
NextSolverConfig { coherence: true, globally: false }
}
}
#[derive(Clone)]
pub enum Input {
File(PathBuf),
Str {
name: FileName,
input: String,
},
}
impl Input {
pub fn filestem(&self) -> &str {
if let Input::File(ifile) = self {
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::QuoteExpansion(_) => 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"),
}
}
pub fn file_for_writing(
&self,
outputs: &OutputFilenames,
flavor: OutputType,
codegen_unit_name: Option<&str>,
) -> PathBuf {
match *self {
OutFileName::Real(ref path) => path.clone(),
OutFileName::Stdout => outputs.temp_path(flavor, codegen_unit_name),
}
}
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_stem: String,
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)))
}
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),
}
}
pub fn temp_path(&self, flavor: OutputType, codegen_unit_name: Option<&str>) -> PathBuf {
let extension = flavor.extension();
self.temp_path_ext(extension, codegen_unit_name)
}
pub fn temp_path_dwo(&self, codegen_unit_name: Option<&str>) -> PathBuf {
self.temp_path_ext(DWARF_OBJECT_EXT, codegen_unit_name)
}
pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf {
let mut extension = String::new();
if let Some(codegen_unit_name) = codegen_unit_name {
extension.push_str(codegen_unit_name);
}
if !ext.is_empty() {
if !extension.is_empty() {
extension.push('.');
extension.push_str(RUST_CGU_EXT);
extension.push('.');
}
extension.push_str(ext);
}
let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
self.with_directory_and_extension(temps_directory, &extension)
}
pub fn with_extension(&self, extension: &str) -> PathBuf {
self.with_directory_and_extension(&self.out_directory, extension)
}
fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf {
let mut path = directory.join(&self.filestem);
path.set_extension(extension);
path
}
pub fn split_dwarf_path(
&self,
split_debuginfo_kind: SplitDebuginfo,
split_dwarf_kind: SplitDwarfKind,
cgu_name: Option<&str>,
) -> Option<PathBuf> {
let obj_out = self.temp_path(OutputType::Object, cgu_name);
let dwo_out = self.temp_path_dwo(cgu_name);
match (split_debuginfo_kind, split_dwarf_kind) {
(SplitDebuginfo::Off, SplitDwarfKind::Single | SplitDwarfKind::Split) => None,
(SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Single) => {
Some(obj_out)
}
(SplitDebuginfo::Packed | SplitDebuginfo::Unpacked, SplitDwarfKind::Split) => {
Some(dwo_out)
}
}
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct RemapPathScopeComponents: u8 {
const MACRO = 1 << 0;
const DIAGNOSTICS = 1 << 1;
const DEBUGINFO = 1 << 3;
const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits();
}
}
pub fn host_tuple() -> &'static str {
(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
},
)
}
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![],
maybe_sysroot: 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,
edition: DEFAULT_EDITION,
json_artifact_notifications: false,
json_unused_externs: JsonUnusedExterns::No,
json_future_incompat: false,
pretty: None,
working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
color: ColorConfig::Auto,
logical_env: FxIndexMap::default(),
verbose: false,
}
}
}
impl Options {
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)
}
pub fn will_create_output_file(&self) -> bool {
!self.unstable_opts.parse_only && self.unstable_opts.ls.is_empty() }
#[inline]
pub fn share_generics(&self) -> bool {
match self.unstable_opts.share_generics {
Some(setting) => setting,
None => match self.optimize {
OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true,
OptLevel::Default | OptLevel::Aggressive => false,
},
}
}
pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion {
self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy)
}
}
impl UnstableOptions {
pub fn 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
}
}
#[derive(Copy, Clone, PartialEq, Hash, Debug, HashStable_Generic)]
pub enum EntryFnType {
Main {
sigpipe: u8,
},
Start,
}
#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
#[derive(HashStable_Generic)]
pub enum CrateType {
Executable,
Dylib,
Rlib,
Staticlib,
Cdylib,
ProcMacro,
}
impl CrateType {
pub fn has_metadata(self) -> bool {
match self {
CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true,
CrateType::Executable | CrateType::Cdylib | CrateType::Staticlib => 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 {
cfg::disallow_cfgs(sess, &user_cfg);
user_cfg.extend(cfg::default_configuration(sess));
user_cfg
}
pub fn build_target_config(early_dcx: &EarlyDiagCtxt, opts: &Options, sysroot: &Path) -> Target {
match Target::search(&opts.target_triple, 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) => early_dcx.early_fatal(format!(
"Error loading target specification: {e}. \
Run `rustc --print target-list` for a list of built-in targets"
)),
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum OptionStability {
Stable,
Unstable,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum OptionKind {
Opt,
Multi,
Flag,
FlagMulti,
}
pub struct RustcOptGroup {
pub name: &'static str,
stability: OptionStability,
kind: OptionKind,
short_name: &'static str,
long_name: &'static str,
desc: &'static str,
value_hint: &'static str,
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),
};
}
}
pub fn make_opt(
stability: OptionStability,
kind: OptionKind,
short_name: &'static str,
long_name: &'static str,
desc: &'static str,
value_hint: &'static str,
) -> RustcOptGroup {
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}."
)
});
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, or all (the default).",
"[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, or dylib (the default).\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",
"Comma separated list of types of output for the compiler to emit",
"[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]",
),
opt(
Stable,
Multi,
"",
"print",
"Compiler information to print on stdout",
"[crate-name|file-names|sysroot|target-libdir|cfg|check-cfg|calling-conventions|\
target-list|target-cpus|target-features|relocation-models|code-models|\
tls-models|target-spec-json|all-target-specs-json|native-static-libs|\
stack-protector-strategies|link-args|deployment-target]",
),
opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""),
opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=2", ""),
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", ""),
];
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(None), lint::Deny, lint::Forbid] {
for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
if lint_name == "help" {
describe_lints = true;
} else {
lint_opts_with_position.push((arg_pos, lint_name.replace('-', "_"), level));
}
}
}
lint_opts_with_position.sort_by_key(|x| x.0);
let lint_opts = lint_opts_with_position
.iter()
.cloned()
.map(|(_, lint_name, level)| (lint_name, level))
.collect();
let lint_cap = matches.opt_str("cap-lints").map(|cap| {
lint::Level::from_str(&cap)
.unwrap_or_else(|| early_dcx.early_fatal(format!("unknown lint level: `{cap}`")))
});
(lint_opts, describe_lints, lint_cap)
}
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}`)"
)),
}
}
pub struct JsonConfig {
pub json_rendered: HumanReadableErrorType,
pub json_color: ColorConfig,
json_artifact_notifications: bool,
pub json_unused_externs: JsonUnusedExterns,
json_future_incompat: bool,
}
#[derive(Copy, Clone)]
pub enum JsonUnusedExterns {
No,
Silent,
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,
}
}
}
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;
for option in matches.opt_strs("json") {
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,
"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_unused_externs,
json_future_incompat,
}
}
pub fn parse_error_format(
early_dcx: &mut EarlyDiagCtxt,
matches: &getopts::Matches,
color: ColorConfig,
json_color: ColorConfig,
json_rendered: HumanReadableErrorType,
) -> ErrorOutputType {
let error_format = if matches.opts_present(&["error-format".to_owned()]) {
match matches.opt_str("error-format").as_deref() {
None | Some("human") => {
ErrorOutputType::HumanReadable(HumanReadableErrorType::Default, color)
}
Some("human-annotate-rs") => {
ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet, color)
}
Some("json") => {
ErrorOutputType::Json { pretty: false, json_rendered, color_config: json_color }
}
Some("pretty-json") => {
ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color }
}
Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short, color),
Some("human-unicode") => {
ErrorOutputType::HumanReadable(HumanReadableErrorType::Unicode, color)
}
Some(arg) => {
early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable(
HumanReadableErrorType::Default,
color,
));
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(HumanReadableErrorType::Default, color)
};
match error_format {
ErrorOutputType::Json { .. } => {}
_ 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(format, _) => match format {
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_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;
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_ref().is_some_and(|s| s == "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();
}
const PRINT_KINDS: &[(&str, PrintKind)] = &[
("all-target-specs-json", PrintKind::AllTargetSpecs),
("calling-conventions", PrintKind::CallingConventions),
("cfg", PrintKind::Cfg),
("check-cfg", PrintKind::CheckCfg),
("code-models", PrintKind::CodeModels),
("crate-name", PrintKind::CrateName),
("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),
("sysroot", PrintKind::Sysroot),
("target-cpus", PrintKind::TargetCPUs),
("target-features", PrintKind::TargetFeatures),
("target-libdir", PrintKind::TargetLibdir),
("target-list", PrintKind::TargetList),
("target-spec-json", PrintKind::TargetSpec),
("tls-models", PrintKind::TlsModels),
];
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 = match PRINT_KINDS.iter().find(|&&(name, _)| name == req) {
Some((_, PrintKind::TargetSpec)) => {
if unstable_opts.unstable_options {
PrintKind::TargetSpec
} else {
early_dcx.early_fatal(
"the `-Z unstable-options` flag must also be passed to \
enable the target-spec-json print option",
);
}
}
Some((_, PrintKind::AllTargetSpecs)) => {
if unstable_opts.unstable_options {
PrintKind::AllTargetSpecs
} else {
early_dcx.early_fatal(
"the `-Z unstable-options` flag must also be passed to \
enable the all-target-specs-json print option",
);
}
}
Some((_, PrintKind::CheckCfg)) => {
if unstable_opts.unstable_options {
PrintKind::CheckCfg
} else {
early_dcx.early_fatal(
"the `-Z unstable-options` flag must also be passed to \
enable the check-cfg print option",
);
}
}
Some(&(_, print_kind)) => print_kind,
None => {
let prints =
PRINT_KINDS.iter().map(|(name, _)| 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}"));
diag.emit()
}
};
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
}
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 {
let max_o = matches.opt_positions("O").into_iter().max();
let max_c = matches
.opt_strs_pos("C")
.into_iter()
.flat_map(|(i, s)| {
if let Some("opt-level") = s.split('=').next() { Some(i) } else { None }
})
.max();
if max_o > max_c {
OptLevel::Default
} else {
match cg.opt_level.as_ref() {
"0" => OptLevel::No,
"1" => OptLevel::Less,
"2" => OptLevel::Default,
"3" => OptLevel::Aggressive,
"s" => OptLevel::Size,
"z" => OptLevel::SizeMin,
arg => {
early_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)| {
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,
}
}
fn parse_native_lib_kind(
early_dcx: &EarlyDiagCtxt,
matches: &getopts::Matches,
kind: &str,
) -> (NativeLibKind, Option<bool>) {
let (kind, modifiers) = match kind.split_once(':') {
None => (kind, None),
Some((kind, modifiers)) => (kind, Some(modifiers)),
};
let kind = match kind {
"static" => NativeLibKind::Static { bundle: None, whole_archive: None },
"dylib" => NativeLibKind::Dylib { as_needed: None },
"framework" => NativeLibKind::Framework { as_needed: None },
"link-arg" => {
if !nightly_options::is_unstable_enabled(matches) {
let why = if nightly_options::match_is_nightly_build(matches) {
" and only accepted on the nightly compiler"
} else {
", the `-Z unstable-options` flag must also be passed to use it"
};
early_dcx.early_fatal(format!("library kind `link-arg` is unstable{why}"))
}
NativeLibKind::LinkArg
}
_ => early_dcx.early_fatal(format!(
"unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
)),
};
match modifiers {
None => (kind, None),
Some(modifiers) => parse_native_lib_modifiers(early_dcx, kind, modifiers, matches),
}
}
fn parse_native_lib_modifiers(
early_dcx: &EarlyDiagCtxt,
mut kind: NativeLibKind,
modifiers: &str,
matches: &getopts::Matches,
) -> (NativeLibKind, Option<bool>) {
let mut verbatim = None;
for modifier in modifiers.split(',') {
let (modifier, value) = match modifier.strip_prefix(['+', '-']) {
Some(m) => (m, modifier.starts_with('+')),
None => early_dcx.early_fatal(
"invalid linking modifier syntax, expected '+' or '-' prefix \
before one of: bundle, verbatim, whole-archive, as-needed",
),
};
let report_unstable_modifier = || {
if !nightly_options::is_unstable_enabled(matches) {
let why = if nightly_options::match_is_nightly_build(matches) {
" and only accepted on the nightly compiler"
} else {
", the `-Z unstable-options` flag must also be passed to use it"
};
early_dcx.early_fatal(format!("linking modifier `{modifier}` is unstable{why}"))
}
};
let assign_modifier = |dst: &mut Option<bool>| {
if dst.is_some() {
let msg = format!("multiple `{modifier}` modifiers in a single `-l` option");
early_dcx.early_fatal(msg)
} else {
*dst = Some(value);
}
};
match (modifier, &mut kind) {
("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle),
("bundle", _) => early_dcx.early_fatal(
"linking modifier `bundle` is only compatible with `static` linking kind",
),
("verbatim", _) => assign_modifier(&mut verbatim),
("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
assign_modifier(whole_archive)
}
("whole-archive", _) => early_dcx.early_fatal(
"linking modifier `whole-archive` is only compatible with `static` linking kind",
),
("as-needed", NativeLibKind::Dylib { as_needed })
| ("as-needed", NativeLibKind::Framework { as_needed }) => {
report_unstable_modifier();
assign_modifier(as_needed)
}
("as-needed", _) => early_dcx.early_fatal(
"linking modifier `as-needed` is only compatible with \
`dylib` and `framework` linking kinds",
),
_ => early_dcx.early_fatal(format!(
"unknown linking modifier `{modifier}`, expected one \
of: bundle, verbatim, whole-archive, as-needed"
)),
}
}
(kind, verbatim)
}
fn parse_libs(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Vec<NativeLib> {
matches
.opt_strs("l")
.into_iter()
.map(|s| {
let (name, kind, verbatim) = match s.split_once('=') {
None => (s, NativeLibKind::Unspecified, None),
Some((kind, name)) => {
let (kind, verbatim) = parse_native_lib_kind(early_dcx, matches, kind);
(name.to_string(), kind, verbatim)
}
};
let (name, new_name) = match name.split_once(':') {
None => (name, None),
Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
};
if name.is_empty() {
early_dcx.early_fatal("library name must not be empty");
}
NativeLib { name, new_name, kind, verbatim }
})
.collect()
}
pub fn parse_externs(
early_dcx: &EarlyDiagCtxt,
matches: &getopts::Matches,
unstable_opts: &UnstableOptions,
) -> Externs {
fn is_ascii_ident(string: &str) -> bool {
let mut chars = string.chars();
if let Some(start) = chars.next()
&& (start.is_ascii_alphabetic() || start == '_')
{
chars.all(|char| char.is_ascii_alphanumeric() || char == '_')
} else {
false
}
}
let is_unstable_enabled = unstable_opts.unstable_options;
let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
for arg in matches.opt_strs("extern") {
let (name, path) = match arg.split_once('=') {
None => (arg, None),
Some((name, path)) => (name.to_string(), Some(Path::new(path))),
};
let (options, name) = match name.split_once(':') {
None => (None, name),
Some((opts, name)) => (Some(opts), name.to_string()),
};
if !is_ascii_ident(&name) {
let mut error = early_dcx.early_struct_fatal(format!(
"crate name `{name}` passed to `--extern` is not a valid ASCII identifier"
));
let adjusted_name = name.replace('-', "_");
if is_ascii_ident(&adjusted_name) {
#[allow(rustc::diagnostic_outside_of_impl)] error.help(format!(
"consider replacing the dashes with underscores: `{adjusted_name}`"
));
}
error.emit();
}
let path = path.map(|p| CanonicalizedPath::new(p));
let entry = externs.entry(name.to_owned());
use std::collections::btree_map::Entry;
let entry = if let Some(path) = path {
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,
..
} => {
let files = BTreeSet::from_iter(iter::once(path));
*location = ExternLocation::ExactPaths(files);
}
}
ext_ent
}
}
} else {
match entry {
Entry::Vacant(vacant) => {
vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories))
}
Entry::Occupied(occupied) => {
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}`")),
}
}
}
entry.is_private_dep |= is_private_dep;
entry.nounused_dep |= nounused_dep;
entry.force |= force;
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
}
#[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_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.abort_if_error_and_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 unstable_opts = UnstableOptions::build(early_dcx, matches);
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
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);
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 fuel = unstable_opts.fuel.is_some() || unstable_opts.print_fuel.is_some();
if fuel && unstable_opts.threads > 1 {
early_dcx.early_fatal("optimization fuel is incompatible with multiple threads");
}
if fuel && cg.incremental.is_some() {
early_dcx.early_fatal("optimization fuel is incompatible with incremental compilation");
}
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`",
);
}
match cg.symbol_mangling_version {
None | Some(SymbolManglingVersion::V0) => {}
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`",
);
}
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") {
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")
}
}
}
if !nightly_options::is_unstable_enabled(matches)
&& 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) {
let uses_unstable_self_contained_option =
cg.link_self_contained.are_unstable_variants_set();
if uses_unstable_self_contained_option {
early_dcx.early_fatal(
"only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off` are stable, \
the `-Z unstable-options` flag must also be passed to use the unstable values",
);
}
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()
));
}
}
}
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);
let cg = cg;
let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
let target_triple = parse_target_triple(early_dcx, matches);
let opt_level = parse_opt_level(early_dcx, matches, &cg);
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;
let libs = parse_libs(early_dcx, 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 crate_name = matches.opt_str("crate-name");
let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts);
let pretty = parse_pretty(early_dcx, &unstable_opts);
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 = filesearch::materialize_sysroot(sysroot_opt);
let real_rust_source_base_dir = {
let mut candidate = sysroot.join("lib/rustlib/src/rust");
if let Ok(metadata) = candidate.symlink_metadata() {
if metadata.file_type().is_symlink() {
if let Ok(symlink_dest) = std::fs::read_link(&candidate) {
candidate = symlink_dest;
}
}
}
candidate.join("library/std/src/lib.rs").is_file().then_some(candidate)
};
let mut search_paths = vec![];
for s in &matches.opt_strs("L") {
search_paths.push(SearchPath::from_cli_opt(
&sysroot,
&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,
maybe_sysroot: Some(sysroot),
target_triple,
test,
incremental,
untracked_state_hash: Default::default(),
unstable_opts,
prints,
cg,
error_format,
diagnostic_width,
externs,
unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()),
crate_name,
libs,
debug_assertions,
actually_rustdoc: false,
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,
edition,
json_artifact_notifications,
json_unused_externs,
json_future_incompat,
pretty,
working_dir,
color,
logical_env,
verbose,
}
}
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,
_ => return Err(format!("unknown crate type: `{part}`")),
};
if !crate_types.contains(&new_part) {
crate_types.push(new_part)
}
}
}
Ok(crate_types)
}
pub mod nightly_options {
use 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
);
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),
}
}
}
impl IntoDiagArg for CrateType {
fn into_diag_arg(self) -> DiagArgValue {
self.to_string().into_diag_arg()
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum PpSourceMode {
Normal,
Expanded,
Identified,
ExpandedIdentified,
ExpandedHygiene,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum PpHirMode {
Normal,
Identified,
Typed,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum PpMode {
Source(PpSourceMode),
AstTree,
AstTreeExpanded,
Hir(PpHirMode),
HirTree,
ThirTree,
ThirFlat,
Mir,
MirCFG,
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_hir(&self) -> bool {
use PpMode::*;
match *self {
Source(_) | AstTree | AstTreeExpanded => false,
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,
}
pub(crate) mod dep_tracking {
use std::collections::BTreeMap;
use std::hash::{DefaultHasher, Hash};
use std::num::NonZero;
use std::path::PathBuf;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stable_hasher::Hash64;
use rustc_errors::LanguageIdentifier;
use rustc_feature::UnstableFeatures;
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, WasmCAbi,
};
use super::{
BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions,
CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn,
InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
LtoCli, NextSolverConfig, 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 DefaultHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
);
}
macro_rules! impl_dep_tracking_hash_via_hash {
($($t:ty),+ $(,)?) => {$(
impl DepTrackingHash for $t {
fn hash(&self, hasher: &mut DefaultHasher, _: ErrorOutputType, _for_crate_hash: bool) {
Hash::hash(self, hasher);
}
}
)+};
}
impl<T: DepTrackingHash> DepTrackingHash for Option<T> {
fn hash(
&self,
hasher: &mut DefaultHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
match self {
Some(x) => {
Hash::hash(&1, hasher);
DepTrackingHash::hash(x, hasher, error_format, for_crate_hash);
}
None => Hash::hash(&0, hasher),
}
}
}
impl_dep_tracking_hash_via_hash!(
bool,
usize,
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,
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,
WasmCAbi,
);
impl<T1, T2> DepTrackingHash for (T1, T2)
where
T1: DepTrackingHash,
T2: DepTrackingHash,
{
fn hash(
&self,
hasher: &mut DefaultHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
Hash::hash(&0, hasher);
DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
Hash::hash(&1, hasher);
DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
}
}
impl<T1, T2, T3> DepTrackingHash for (T1, T2, T3)
where
T1: DepTrackingHash,
T2: DepTrackingHash,
T3: DepTrackingHash,
{
fn hash(
&self,
hasher: &mut DefaultHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
Hash::hash(&0, hasher);
DepTrackingHash::hash(&self.0, hasher, error_format, for_crate_hash);
Hash::hash(&1, hasher);
DepTrackingHash::hash(&self.1, hasher, error_format, for_crate_hash);
Hash::hash(&2, hasher);
DepTrackingHash::hash(&self.2, hasher, error_format, for_crate_hash);
}
}
impl<T: DepTrackingHash> DepTrackingHash for Vec<T> {
fn hash(
&self,
hasher: &mut DefaultHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
Hash::hash(&self.len(), hasher);
for (index, elem) in self.iter().enumerate() {
Hash::hash(&index, hasher);
DepTrackingHash::hash(elem, hasher, error_format, for_crate_hash);
}
}
}
impl<T: DepTrackingHash, V: DepTrackingHash> DepTrackingHash for FxIndexMap<T, V> {
fn hash(
&self,
hasher: &mut DefaultHasher,
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 DefaultHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
Hash::hash(&self.0.len(), hasher);
for (key, val) in &self.0 {
DepTrackingHash::hash(key, hasher, error_format, for_crate_hash);
if !for_crate_hash {
DepTrackingHash::hash(val, hasher, error_format, for_crate_hash);
}
}
}
}
pub(crate) fn stable_hash(
sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>,
hasher: &mut DefaultHasher,
error_format: ErrorOutputType,
for_crate_hash: bool,
) {
for (key, sub_hash) in sub_hashes {
Hash::hash(&key.len(), hasher);
Hash::hash(key, hasher);
sub_hash.hash(hasher, error_format, for_crate_hash);
}
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug, Encodable, Decodable, HashStable_Generic)]
pub enum OomStrategy {
Panic,
Abort,
}
impl OomStrategy {
pub const SYMBOL: &'static str = "__rust_alloc_error_handler_should_panic";
pub fn should_panic(self) -> u8 {
match self {
OomStrategy::Panic => 1,
OomStrategy::Abort => 0,
}
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum ProcMacroExecutionStrategy {
SameThread,
CrossThread,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum CollapseMacroDebuginfo {
No = 0,
Unspecified = 1,
External = 2,
Yes = 3,
}
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum DumpMonoStatsFormat {
Markdown,
Json,
}
impl DumpMonoStatsFormat {
pub fn extension(self) -> &'static str {
match self {
Self::Markdown => "md",
Self::Json => "json",
}
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
pub struct PatchableFunctionEntry {
prefix: u8,
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
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
pub enum Polonius {
#[default]
Off,
Legacy,
Next,
}
impl Polonius {
pub fn is_legacy_enabled(&self) -> bool {
matches!(self, Polonius::Legacy)
}
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)
}
}
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
pub enum FunctionReturn {
#[default]
Keep,
ThunkExtern,
}
#[derive(Clone, Copy, Default, PartialEq, Debug)]
pub enum MirIncludeSpans {
Off,
On,
#[default]
Nll,
}
impl MirIncludeSpans {
pub fn is_enabled(self) -> bool {
self == MirIncludeSpans::On
}
}