diff options
Diffstat (limited to 'compiler/rustc_session')
| -rw-r--r-- | compiler/rustc_session/Cargo.toml | 20 | ||||
| -rw-r--r-- | compiler/rustc_session/src/cgu_reuse_tracker.rs | 120 | ||||
| -rw-r--r-- | compiler/rustc_session/src/code_stats.rs | 193 | ||||
| -rw-r--r-- | compiler/rustc_session/src/config.rs | 2186 | ||||
| -rw-r--r-- | compiler/rustc_session/src/filesearch.rs | 171 | ||||
| -rw-r--r-- | compiler/rustc_session/src/lib.rs | 27 | ||||
| -rw-r--r-- | compiler/rustc_session/src/lint.rs | 425 | ||||
| -rw-r--r-- | compiler/rustc_session/src/lint/builtin.rs | 624 | ||||
| -rw-r--r-- | compiler/rustc_session/src/options.rs | 1078 | ||||
| -rw-r--r-- | compiler/rustc_session/src/output.rs | 217 | ||||
| -rw-r--r-- | compiler/rustc_session/src/parse.rs | 239 | ||||
| -rw-r--r-- | compiler/rustc_session/src/search_paths.rs | 95 | ||||
| -rw-r--r-- | compiler/rustc_session/src/session.rs | 1539 | ||||
| -rw-r--r-- | compiler/rustc_session/src/utils.rs | 32 |
14 files changed, 6966 insertions, 0 deletions
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml new file mode 100644 index 00000000000..cdff1662fdb --- /dev/null +++ b/compiler/rustc_session/Cargo.toml @@ -0,0 +1,20 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_session" +version = "0.0.0" +edition = "2018" + +[dependencies] +bitflags = "1.2.1" +getopts = "0.2" +rustc_macros = { path = "../rustc_macros" } +tracing = "0.1" +rustc_errors = { path = "../rustc_errors" } +rustc_feature = { path = "../rustc_feature" } +rustc_target = { path = "../rustc_target" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_span = { path = "../rustc_span" } +rustc_fs_util = { path = "../rustc_fs_util" } +num_cpus = "1.0" +rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_session/src/cgu_reuse_tracker.rs b/compiler/rustc_session/src/cgu_reuse_tracker.rs new file mode 100644 index 00000000000..0eec12aa03f --- /dev/null +++ b/compiler/rustc_session/src/cgu_reuse_tracker.rs @@ -0,0 +1,120 @@ +//! Some facilities for tracking how codegen-units are reused during incremental +//! compilation. This is used for incremental compilation tests and debug +//! output. + +use rustc_data_structures::fx::FxHashMap; +use rustc_span::{Span, Symbol}; +use std::sync::{Arc, Mutex}; +use tracing::debug; + +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +pub enum CguReuse { + No, + PreLto, + PostLto, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ComparisonKind { + Exact, + AtLeast, +} + +struct TrackerData { + actual_reuse: FxHashMap<String, CguReuse>, + expected_reuse: FxHashMap<String, (String, SendSpan, CguReuse, ComparisonKind)>, +} + +// Span does not implement `Send`, so we can't just store it in the shared +// `TrackerData` object. Instead of splitting up `TrackerData` into shared and +// non-shared parts (which would be complicated), we just mark the `Span` here +// explicitly as `Send`. That's safe because the span data here is only ever +// accessed from the main thread. +struct SendSpan(Span); +unsafe impl Send for SendSpan {} + +#[derive(Clone)] +pub struct CguReuseTracker { + data: Option<Arc<Mutex<TrackerData>>>, +} + +impl CguReuseTracker { + pub fn new() -> CguReuseTracker { + let data = + TrackerData { actual_reuse: Default::default(), expected_reuse: Default::default() }; + + CguReuseTracker { data: Some(Arc::new(Mutex::new(data))) } + } + + pub fn new_disabled() -> CguReuseTracker { + CguReuseTracker { data: None } + } + + pub fn set_actual_reuse(&self, cgu_name: &str, kind: CguReuse) { + if let Some(ref data) = self.data { + debug!("set_actual_reuse({:?}, {:?})", cgu_name, kind); + + let prev_reuse = data.lock().unwrap().actual_reuse.insert(cgu_name.to_string(), kind); + + if let Some(prev_reuse) = prev_reuse { + // The only time it is legal to overwrite reuse state is when + // we discover during ThinLTO that we can actually reuse the + // post-LTO version of a CGU. + assert_eq!(prev_reuse, CguReuse::PreLto); + } + } + } + + pub fn set_expectation( + &self, + cgu_name: Symbol, + cgu_user_name: &str, + error_span: Span, + expected_reuse: CguReuse, + comparison_kind: ComparisonKind, + ) { + if let Some(ref data) = self.data { + debug!("set_expectation({:?}, {:?}, {:?})", cgu_name, expected_reuse, comparison_kind); + let mut data = data.lock().unwrap(); + + data.expected_reuse.insert( + cgu_name.to_string(), + (cgu_user_name.to_string(), SendSpan(error_span), expected_reuse, comparison_kind), + ); + } + } + + pub fn check_expected_reuse(&self, diag: &rustc_errors::Handler) { + if let Some(ref data) = self.data { + let data = data.lock().unwrap(); + + for (cgu_name, &(ref cgu_user_name, ref error_span, expected_reuse, comparison_kind)) in + &data.expected_reuse + { + if let Some(&actual_reuse) = data.actual_reuse.get(cgu_name) { + let (error, at_least) = match comparison_kind { + ComparisonKind::Exact => (expected_reuse != actual_reuse, false), + ComparisonKind::AtLeast => (actual_reuse < expected_reuse, true), + }; + + if error { + let at_least = if at_least { "at least " } else { "" }; + let msg = format!( + "CGU-reuse for `{}` is `{:?}` but \ + should be {}`{:?}`", + cgu_user_name, actual_reuse, at_least, expected_reuse + ); + diag.span_err(error_span.0, &msg); + } + } else { + let msg = format!( + "CGU-reuse for `{}` (mangled: `{}`) was \ + not recorded", + cgu_user_name, cgu_name + ); + diag.span_fatal(error_span.0, &msg).raise(); + } + } + } + } +} diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs new file mode 100644 index 00000000000..c263da69c35 --- /dev/null +++ b/compiler/rustc_session/src/code_stats.rs @@ -0,0 +1,193 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sync::Lock; +use rustc_target::abi::{Align, Size}; +use std::cmp::{self, Ordering}; + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct VariantInfo { + pub name: Option<String>, + pub kind: SizeKind, + pub size: u64, + pub align: u64, + pub fields: Vec<FieldInfo>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum SizeKind { + Exact, + Min, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct FieldInfo { + pub name: String, + pub offset: u64, + pub size: u64, + pub align: u64, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum DataTypeKind { + Struct, + Union, + Enum, + Closure, +} + +#[derive(PartialEq, Eq, Hash, Debug)] +pub struct TypeSizeInfo { + pub kind: DataTypeKind, + pub type_description: String, + pub align: u64, + pub overall_size: u64, + pub packed: bool, + pub opt_discr_size: Option<u64>, + pub variants: Vec<VariantInfo>, +} + +#[derive(Default)] +pub struct CodeStats { + type_sizes: Lock<FxHashSet<TypeSizeInfo>>, +} + +impl CodeStats { + pub fn record_type_size<S: ToString>( + &self, + kind: DataTypeKind, + type_desc: S, + align: Align, + overall_size: Size, + packed: bool, + opt_discr_size: Option<Size>, + mut variants: Vec<VariantInfo>, + ) { + // Sort variants so the largest ones are shown first. A stable sort is + // used here so that source code order is preserved for all variants + // that have the same size. + variants.sort_by(|info1, info2| info2.size.cmp(&info1.size)); + let info = TypeSizeInfo { + kind, + type_description: type_desc.to_string(), + align: align.bytes(), + overall_size: overall_size.bytes(), + packed, + opt_discr_size: opt_discr_size.map(|s| s.bytes()), + variants, + }; + self.type_sizes.borrow_mut().insert(info); + } + + pub fn print_type_sizes(&self) { + let type_sizes = self.type_sizes.borrow(); + let mut sorted: Vec<_> = type_sizes.iter().collect(); + + // Primary sort: large-to-small. + // Secondary sort: description (dictionary order) + sorted.sort_by(|info1, info2| { + // (reversing cmp order to get large-to-small ordering) + match info2.overall_size.cmp(&info1.overall_size) { + Ordering::Equal => info1.type_description.cmp(&info2.type_description), + other => other, + } + }); + + for info in &sorted { + println!( + "print-type-size type: `{}`: {} bytes, alignment: {} bytes", + info.type_description, info.overall_size, info.align + ); + let indent = " "; + + let discr_size = if let Some(discr_size) = info.opt_discr_size { + println!("print-type-size {}discriminant: {} bytes", indent, discr_size); + discr_size + } else { + 0 + }; + + // We start this at discr_size (rather than 0) because + // things like C-enums do not have variants but we still + // want the max_variant_size at the end of the loop below + // to reflect the presence of the discriminant. + let mut max_variant_size = discr_size; + + let struct_like = match info.kind { + DataTypeKind::Struct | DataTypeKind::Closure => true, + DataTypeKind::Enum | DataTypeKind::Union => false, + }; + for (i, variant_info) in info.variants.iter().enumerate() { + let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info; + let indent = if !struct_like { + let name = match name.as_ref() { + Some(name) => name.to_owned(), + None => i.to_string(), + }; + println!( + "print-type-size {}variant `{}`: {} bytes", + indent, + name, + size - discr_size + ); + " " + } else { + assert!(i < 1); + " " + }; + max_variant_size = cmp::max(max_variant_size, size); + + let mut min_offset = discr_size; + + // We want to print fields by increasing offset. We also want + // zero-sized fields before non-zero-sized fields, otherwise + // the loop below goes wrong; hence the `f.size` in the sort + // key. + let mut fields = fields.clone(); + fields.sort_by_key(|f| (f.offset, f.size)); + + for field in fields.iter() { + let FieldInfo { ref name, offset, size, align } = *field; + + if offset > min_offset { + let pad = offset - min_offset; + println!("print-type-size {}padding: {} bytes", indent, pad); + } + + if offset < min_offset { + // If this happens it's probably a union. + println!( + "print-type-size {}field `.{}`: {} bytes, \ + offset: {} bytes, \ + alignment: {} bytes", + indent, name, size, offset, align + ); + } else if info.packed || offset == min_offset { + println!("print-type-size {}field `.{}`: {} bytes", indent, name, size); + } else { + // Include field alignment in output only if it caused padding injection + println!( + "print-type-size {}field `.{}`: {} bytes, \ + alignment: {} bytes", + indent, name, size, align + ); + } + + min_offset = offset + size; + } + } + + assert!( + max_variant_size <= info.overall_size, + "max_variant_size {} !<= {} overall_size", + max_variant_size, + info.overall_size + ); + if max_variant_size < info.overall_size { + println!( + "print-type-size {}end padding: {} bytes", + indent, + info.overall_size - max_variant_size + ); + } + } + } +} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs new file mode 100644 index 00000000000..1808a0ca59b --- /dev/null +++ b/compiler/rustc_session/src/config.rs @@ -0,0 +1,2186 @@ +//! Contains infrastructure for configuring the compiler, including parsing +//! command-line options. + +pub use crate::options::*; + +use crate::lint; +use crate::search_paths::SearchPath; +use crate::utils::NativeLibKind; +use crate::{early_error, early_warn, Session}; + +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::impl_stable_hash_via_hash; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; + +use rustc_target::spec::{Target, TargetTriple}; + +use crate::parse::CrateConfig; +use rustc_feature::UnstableFeatures; +use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST}; +use rustc_span::source_map::{FileName, FilePathMapping}; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::SourceFileHashAlgorithm; + +use rustc_errors::emitter::HumanReadableErrorType; +use rustc_errors::{ColorConfig, HandlerFlags}; + +use std::collections::btree_map::{ + Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, +}; +use std::collections::{BTreeMap, BTreeSet}; +use std::fmt; +use std::iter::{self, FromIterator}; +use std::path::{Path, PathBuf}; +use std::str::{self, FromStr}; + +pub struct Config { + pub target: Target, + pub ptr_width: u32, +} + +bitflags! { + #[derive(Default, Encodable, Decodable)] + pub struct SanitizerSet: u8 { + const ADDRESS = 1 << 0; + const LEAK = 1 << 1; + const MEMORY = 1 << 2; + const THREAD = 1 << 3; + } +} + +/// Formats a sanitizer set as a comma separated list of sanitizers' names. +impl fmt::Display for SanitizerSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut first = true; + for s in *self { + let name = match s { + SanitizerSet::ADDRESS => "address", + SanitizerSet::LEAK => "leak", + SanitizerSet::MEMORY => "memory", + SanitizerSet::THREAD => "thread", + _ => panic!("unrecognized sanitizer {:?}", s), + }; + if !first { + f.write_str(",")?; + } + f.write_str(name)?; + first = false; + } + Ok(()) + } +} + +impl IntoIterator for SanitizerSet { + type Item = SanitizerSet; + type IntoIter = std::vec::IntoIter<SanitizerSet>; + + fn into_iter(self) -> Self::IntoIter { + [SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD] + .iter() + .copied() + .filter(|&s| self.contains(s)) + .collect::<Vec<_>>() + .into_iter() + } +} + +impl<CTX> HashStable<CTX> for SanitizerSet { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + self.bits().hash_stable(ctx, hasher); + } +} + +/// The different settings that the `-Z 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, +} + +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum OptLevel { + No, // -O0 + Less, // -O1 + Default, // -O2 + Aggressive, // -O3 + Size, // -Os + SizeMin, // -Oz +} + +impl_stable_hash_via_hash!(OptLevel); + +/// This is what the `LtoCli` values get mapped to after resolving defaults and +/// and taking other command line options into account. +#[derive(Clone, PartialEq)] +pub enum Lto { + /// Don't do any LTO whatsoever + No, + + /// Do a full crate graph LTO with ThinLTO + Thin, + + /// Do a local graph LTO with ThinLTO (only relevant for multiple codegen + /// units). + ThinLocal, + + /// Do a full crate graph 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, +} + +#[derive(Clone, PartialEq, Hash)] +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(Clone, PartialEq, Hash)] +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)] +#[derive(Encodable, Decodable)] +pub enum SymbolManglingVersion { + Legacy, + V0, +} + +impl_stable_hash_via_hash!(SymbolManglingVersion); + +#[derive(Clone, Copy, PartialEq, Hash)] +pub enum DebugInfo { + None, + Limited, + Full, +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] +#[derive(Encodable, Decodable)] +pub enum OutputType { + Bitcode, + Assembly, + LlvmAssembly, + Mir, + Metadata, + Object, + Exe, + DepInfo, +} + +impl_stable_hash_via_hash!(OutputType); + +impl OutputType { + fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool { + match *self { + OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true, + OutputType::Bitcode + | OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::Object => false, + } + } + + fn shorthand(&self) -> &'static str { + match *self { + OutputType::Bitcode => "llvm-bc", + OutputType::Assembly => "asm", + OutputType::LlvmAssembly => "llvm-ir", + OutputType::Mir => "mir", + OutputType::Object => "obj", + OutputType::Metadata => "metadata", + OutputType::Exe => "link", + OutputType::DepInfo => "dep-info", + } + } + + fn from_shorthand(shorthand: &str) -> Option<Self> { + Some(match shorthand { + "asm" => OutputType::Assembly, + "llvm-ir" => OutputType::LlvmAssembly, + "mir" => OutputType::Mir, + "llvm-bc" => OutputType::Bitcode, + "obj" => OutputType::Object, + "metadata" => OutputType::Metadata, + "link" => OutputType::Exe, + "dep-info" => OutputType::DepInfo, + _ => return None, + }) + } + + fn shorthands_display() -> String { + format!( + "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`", + OutputType::Bitcode.shorthand(), + OutputType::Assembly.shorthand(), + OutputType::LlvmAssembly.shorthand(), + OutputType::Mir.shorthand(), + OutputType::Object.shorthand(), + OutputType::Metadata.shorthand(), + OutputType::Exe.shorthand(), + OutputType::DepInfo.shorthand(), + ) + } + + pub fn extension(&self) -> &'static str { + match *self { + OutputType::Bitcode => "bc", + OutputType::Assembly => "s", + OutputType::LlvmAssembly => "ll", + OutputType::Mir => "mir", + OutputType::Object => "o", + OutputType::Metadata => "rmeta", + OutputType::DepInfo => "d", + OutputType::Exe => "", + } + } +} + +/// The type of diagnostics output to generate. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ErrorOutputType { + /// Output meant for the consumption of humans. + HumanReadable(HumanReadableErrorType), + /// Output that's consumed by other tools such as `rustfix` or the `RLS`. + Json { + /// Render the JSON in a human readable way (with indents and newlines). + pretty: bool, + /// The JSON output includes a `rendered` field that includes the rendered + /// human output. + json_rendered: HumanReadableErrorType, + }, +} + +impl Default for ErrorOutputType { + fn default() -> Self { + Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto)) + } +} + +/// 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. +#[derive(Clone, Hash)] +pub struct OutputTypes(BTreeMap<OutputType, Option<PathBuf>>); + +impl_stable_hash_via_hash!(OutputTypes); + +impl OutputTypes { + pub fn new(entries: &[(OutputType, Option<PathBuf>)]) -> OutputTypes { + OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone())))) + } + + pub fn get(&self, key: &OutputType) -> Option<&Option<PathBuf>> { + self.0.get(key) + } + + pub fn contains_key(&self, key: &OutputType) -> bool { + self.0.contains_key(key) + } + + pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option<PathBuf>> { + self.0.keys() + } + + pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option<PathBuf>> { + self.0.values() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + // Returns `true` if any of the output types require codegen or linking. + pub fn should_codegen(&self) -> bool { + self.0.keys().any(|k| match *k { + OutputType::Bitcode + | OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::Object + | OutputType::Exe => true, + OutputType::Metadata | OutputType::DepInfo => false, + }) + } +} + +/// 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, +} + +#[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<String>), +} + +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 } + } + + pub fn files(&self) -> Option<impl Iterator<Item = &String>> { + match &self.location { + ExternLocation::ExactPaths(set) => Some(set.iter()), + _ => None, + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum PrintRequest { + FileNames, + Sysroot, + TargetLibdir, + CrateName, + Cfg, + TargetList, + TargetCPUs, + TargetFeatures, + RelocationModels, + CodeModels, + TlsModels, + TargetSpec, + NativeStaticLibs, +} + +#[derive(Copy, Clone)] +pub enum BorrowckMode { + Mir, + Migrate, +} + +impl BorrowckMode { + /// Returns whether we should run the MIR-based borrow check, but also fall back + /// on the AST borrow check if the MIR-based one errors. + pub fn migrate(self) -> bool { + match self { + BorrowckMode::Mir => false, + BorrowckMode::Migrate => true, + } + } +} + +pub enum Input { + /// Load source code from a file. + File(PathBuf), + /// Load source code from a string. + Str { + /// A string that is shown in place of a filename. + name: FileName, + /// An anonymous string containing the source code. + input: String, + }, +} + +impl Input { + pub fn filestem(&self) -> &str { + match *self { + Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(), + Input::Str { .. } => "rust_out", + } + } + + pub fn get_input(&mut self) -> Option<&mut String> { + match *self { + Input::File(_) => None, + Input::Str { ref mut input, .. } => Some(input), + } + } + + pub fn source_name(&self) -> FileName { + match *self { + Input::File(ref ifile) => ifile.clone().into(), + Input::Str { ref name, .. } => name.clone(), + } + } +} + +#[derive(Clone, Hash)] +pub struct OutputFilenames { + pub out_directory: PathBuf, + filestem: String, + pub single_output_file: Option<PathBuf>, + pub outputs: OutputTypes, +} + +impl_stable_hash_via_hash!(OutputFilenames); + +pub const RLINK_EXT: &str = "rlink"; +pub const RUST_CGU_EXT: &str = "rcgu"; + +impl OutputFilenames { + pub fn new( + out_directory: PathBuf, + out_filestem: String, + single_output_file: Option<PathBuf>, + extra: String, + outputs: OutputTypes, + ) -> Self { + OutputFilenames { + out_directory, + single_output_file, + outputs, + filestem: format!("{}{}", out_filestem, extra), + } + } + + pub fn path(&self, flavor: OutputType) -> PathBuf { + self.outputs + .get(&flavor) + .and_then(|p| p.to_owned()) + .or_else(|| self.single_output_file.clone()) + .unwrap_or_else(|| self.temp_path(flavor, None)) + } + + /// Gets the path where a compilation artifact of the given type for the + /// given codegen unit should be placed on disk. If codegen_unit_name is + /// None, a path distinct from those of any codegen unit will be generated. + pub fn temp_path(&self, flavor: OutputType, codegen_unit_name: Option<&str>) -> PathBuf { + let extension = flavor.extension(); + self.temp_path_ext(extension, codegen_unit_name) + } + + /// Like temp_path, but also supports things where there is no corresponding + /// OutputType, like noopt-bitcode or lto-bitcode. + pub fn temp_path_ext(&self, ext: &str, codegen_unit_name: Option<&str>) -> PathBuf { + let mut extension = String::new(); + + if let Some(codegen_unit_name) = codegen_unit_name { + extension.push_str(codegen_unit_name); + } + + if !ext.is_empty() { + if !extension.is_empty() { + extension.push_str("."); + extension.push_str(RUST_CGU_EXT); + extension.push_str("."); + } + + extension.push_str(ext); + } + + self.with_extension(&extension) + } + + pub fn with_extension(&self, extension: &str) -> PathBuf { + let mut path = self.out_directory.join(&self.filestem); + path.set_extension(extension); + path + } +} + +pub fn host_triple() -> &'static str { + // Get the host triple out of the build environment. This ensures that our + // idea of the host triple is the same as for the set of libraries we've + // actually built. We can't just take LLVM's host triple because they + // normalize all ix86 architectures to i386. + // + // Instead of grabbing the host triple (for the current host), we grab (at + // compile time) the target triple that this rustc is built with and + // calling that (at runtime) the host triple. + (option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE") +} + +impl Default for Options { + fn default() -> Options { + Options { + crate_types: Vec::new(), + optimize: OptLevel::No, + debuginfo: DebugInfo::None, + lint_opts: Vec::new(), + lint_cap: None, + describe_lints: false, + output_types: OutputTypes(BTreeMap::new()), + search_paths: vec![], + maybe_sysroot: None, + target_triple: TargetTriple::from_triple(host_triple()), + test: false, + incremental: None, + debugging_opts: basic_debugging_options(), + prints: Vec::new(), + borrowck_mode: BorrowckMode::Migrate, + cg: basic_codegen_options(), + error_format: ErrorOutputType::default(), + externs: Externs(BTreeMap::new()), + crate_name: None, + alt_std_name: None, + libs: Vec::new(), + unstable_features: UnstableFeatures::Disallow, + debug_assertions: true, + actually_rustdoc: false, + cli_forced_codegen_units: None, + cli_forced_thinlto_off: false, + remap_path_prefix: Vec::new(), + edition: DEFAULT_EDITION, + json_artifact_notifications: false, + pretty: None, + } + } +} + +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.debugging_opts.dump_dep_graph + || self.debugging_opts.query_dep_graph + } + + #[inline(always)] + pub fn enable_dep_node_debug_strs(&self) -> bool { + cfg!(debug_assertions) + && (self.debugging_opts.query_dep_graph || self.debugging_opts.incremental_info) + } + + pub fn file_path_mapping(&self) -> FilePathMapping { + FilePathMapping::new(self.remap_path_prefix.clone()) + } + + /// Returns `true` if there will be an output file generated. + pub fn will_create_output_file(&self) -> bool { + !self.debugging_opts.parse_only && // The file is just being parsed + !self.debugging_opts.ls // The file is just being queried + } + + #[inline] + pub fn share_generics(&self) -> bool { + match self.debugging_opts.share_generics { + Some(setting) => setting, + None => match self.optimize { + OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true, + OptLevel::Default | OptLevel::Aggressive => false, + }, + } + } +} + +impl DebuggingOptions { + pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags { + HandlerFlags { + can_emit_warnings, + treat_err_as_bug: self.treat_err_as_bug, + dont_buffer_diagnostics: self.dont_buffer_diagnostics, + report_delayed_bugs: self.report_delayed_bugs, + macro_backtrace: self.macro_backtrace, + deduplicate_diagnostics: self.deduplicate_diagnostics, + } + } +} + +// The type of entry function, so users can have their own entry functions +#[derive(Copy, Clone, PartialEq, Hash, Debug)] +pub enum EntryFnType { + Main, + Start, +} + +impl_stable_hash_via_hash!(EntryFnType); + +#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] +pub enum CrateType { + Executable, + Dylib, + Rlib, + Staticlib, + Cdylib, + ProcMacro, +} + +impl_stable_hash_via_hash!(CrateType); + +#[derive(Clone, Hash)] +pub enum Passes { + Some(Vec<String>), + All, +} + +impl Passes { + pub fn is_empty(&self) -> bool { + match *self { + Passes::Some(ref v) => v.is_empty(), + Passes::All => false, + } + } +} + +pub const fn default_lib_output() -> CrateType { + CrateType::Rlib +} + +pub fn default_configuration(sess: &Session) -> CrateConfig { + let end = &sess.target.target.target_endian; + let arch = &sess.target.target.arch; + let wordsz = &sess.target.target.target_pointer_width; + let os = &sess.target.target.target_os; + let env = &sess.target.target.target_env; + let vendor = &sess.target.target.target_vendor; + let min_atomic_width = sess.target.target.min_atomic_width(); + let max_atomic_width = sess.target.target.max_atomic_width(); + let atomic_cas = sess.target.target.options.atomic_cas; + + let mut ret = FxHashSet::default(); + ret.reserve(6); // the minimum number of insertions + // Target bindings. + ret.insert((sym::target_os, Some(Symbol::intern(os)))); + if let Some(ref fam) = sess.target.target.options.target_family { + ret.insert((sym::target_family, Some(Symbol::intern(fam)))); + if fam == "windows" { + ret.insert((sym::windows, None)); + } else if fam == "unix" { + ret.insert((sym::unix, None)); + } + } + ret.insert((sym::target_arch, Some(Symbol::intern(arch)))); + ret.insert((sym::target_endian, Some(Symbol::intern(end)))); + ret.insert((sym::target_pointer_width, Some(Symbol::intern(wordsz)))); + ret.insert((sym::target_env, Some(Symbol::intern(env)))); + ret.insert((sym::target_vendor, Some(Symbol::intern(vendor)))); + if sess.target.target.options.has_elf_tls { + ret.insert((sym::target_thread_local, None)); + } + for &i in &[8, 16, 32, 64, 128] { + if i >= min_atomic_width && i <= max_atomic_width { + let mut insert_atomic = |s| { + ret.insert((sym::target_has_atomic_load_store, Some(Symbol::intern(s)))); + if atomic_cas { + ret.insert((sym::target_has_atomic, Some(Symbol::intern(s)))); + } + }; + let s = i.to_string(); + insert_atomic(&s); + if &s == wordsz { + insert_atomic("ptr"); + } + } + } + + for s in sess.opts.debugging_opts.sanitizer { + let symbol = Symbol::intern(&s.to_string()); + ret.insert((sym::sanitize, Some(symbol))); + } + + if sess.opts.debug_assertions { + ret.insert((sym::debug_assertions, None)); + } + if sess.opts.crate_types.contains(&CrateType::ProcMacro) { + ret.insert((sym::proc_macro, None)); + } + ret +} + +/// Converts the crate `cfg!` configuration from `String` to `Symbol`. +/// `rustc_interface::interface::Config` accepts this in the compiler configuration, +/// but the symbol interner is not yet set up then, so we must convert it later. +pub fn to_crate_config(cfg: FxHashSet<(String, Option<String>)>) -> CrateConfig { + cfg.into_iter().map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))).collect() +} + +pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig { + // Combine the configuration requested by the session (command line) with + // some default and generated configuration items. + let default_cfg = default_configuration(sess); + // If the user wants a test runner, then add the test cfg. + if sess.opts.test { + user_cfg.insert((sym::test, None)); + } + user_cfg.extend(default_cfg.iter().cloned()); + user_cfg +} + +pub fn build_target_config(opts: &Options, error_format: ErrorOutputType) -> Config { + let target = Target::search(&opts.target_triple).unwrap_or_else(|e| { + early_error( + error_format, + &format!( + "Error loading target specification: {}. \ + Use `--print target-list` for a list of built-in targets", + e + ), + ) + }); + + let ptr_width = match &target.target_pointer_width[..] { + "16" => 16, + "32" => 32, + "64" => 64, + w => early_error( + error_format, + &format!( + "target specification was invalid: \ + unrecognized target-pointer-width {}", + w + ), + ), + }; + + Config { target, ptr_width } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum OptionStability { + Stable, + Unstable, +} + +pub struct RustcOptGroup { + pub apply: Box<dyn Fn(&mut getopts::Options) -> &mut getopts::Options>, + pub name: &'static str, + pub stability: OptionStability, +} + +impl RustcOptGroup { + pub fn is_stable(&self) -> bool { + self.stability == OptionStability::Stable + } + + pub fn stable<F>(name: &'static str, f: F) -> RustcOptGroup + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Stable } + } + + pub fn unstable<F>(name: &'static str, f: F) -> RustcOptGroup + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup { name, apply: Box::new(f), stability: OptionStability::Unstable } + } +} + +// The `opt` local module holds wrappers around the `getopts` API that +// adds extra rustc-specific metadata to each option; such metadata +// is exposed by . The public +// functions below ending with `_u` are the functions that return +// *unstable* options, i.e., options that are only enabled when the +// user also passes the `-Z unstable-options` debugging flag. +mod opt { + // The `fn flag*` etc below are written so that we can use them + // in the future; do not warn about them not being used right now. + #![allow(dead_code)] + + use super::RustcOptGroup; + + pub type R = RustcOptGroup; + pub type S = &'static str; + + fn stable<F>(name: S, f: F) -> R + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup::stable(name, f) + } + + fn unstable<F>(name: S, f: F) -> R + where + F: Fn(&mut getopts::Options) -> &mut getopts::Options + 'static, + { + RustcOptGroup::unstable(name, f) + } + + fn longer(a: S, b: S) -> S { + if a.len() > b.len() { a } else { b } + } + + pub fn opt_s(a: S, b: S, c: S, d: S) -> R { + stable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) + } + pub fn multi_s(a: S, b: S, c: S, d: S) -> R { + stable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) + } + pub fn flag_s(a: S, b: S, c: S) -> R { + stable(longer(a, b), move |opts| opts.optflag(a, b, c)) + } + pub fn flagopt_s(a: S, b: S, c: S, d: S) -> R { + stable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) + } + pub fn flagmulti_s(a: S, b: S, c: S) -> R { + stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) + } + + pub fn opt(a: S, b: S, c: S, d: S) -> R { + unstable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) + } + pub fn multi(a: S, b: S, c: S, d: S) -> R { + unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) + } + pub fn flag(a: S, b: S, c: S) -> R { + unstable(longer(a, b), move |opts| opts.optflag(a, b, c)) + } + pub fn flagopt(a: S, b: S, c: S, d: S) -> R { + unstable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) + } + pub fn flagmulti(a: S, b: S, c: S) -> R { + unstable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) + } +} + +/// Returns the "short" subset of the rustc command line options, +/// including metadata for each option, such as whether the option is +/// part of the stable long-term interface for rustc. +pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> { + vec![ + opt::flag_s("h", "help", "Display this message"), + opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"), + opt::multi_s( + "L", + "", + "Add a directory to the library search path. The + optional KIND can be one of dependency, crate, native, + framework, or all (the default).", + "[KIND=]PATH", + ), + opt::multi_s( + "l", + "", + "Link the generated crate(s) to the specified native + library NAME. The optional KIND can be one of + static, framework, or dylib (the default).", + "[KIND=]NAME", + ), + make_crate_type_option(), + opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"), + opt::opt_s( + "", + "edition", + "Specify which edition of the compiler to use when compiling code.", + EDITION_NAME_LIST, + ), + opt::multi_s( + "", + "emit", + "Comma separated list of types of output for \ + the compiler to emit", + "[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]", + ), + opt::multi_s( + "", + "print", + "Compiler information to print on stdout", + "[crate-name|file-names|sysroot|target-libdir|cfg|target-list|\ + target-cpus|target-features|relocation-models|\ + code-models|tls-models|target-spec-json|native-static-libs]", + ), + opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), + opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), + opt::opt_s("o", "", "Write output to <filename>", "FILENAME"), + opt::opt_s( + "", + "out-dir", + "Write output to compiler-chosen filename \ + in <dir>", + "DIR", + ), + opt::opt_s( + "", + "explain", + "Provide a detailed explanation of an error \ + message", + "OPT", + ), + opt::flag_s("", "test", "Build a test harness"), + opt::opt_s("", "target", "Target triple for which the code is compiled", "TARGET"), + opt::multi_s("W", "warn", "Set lint warnings", "OPT"), + opt::multi_s("A", "allow", "Set lint allowed", "OPT"), + opt::multi_s("D", "deny", "Set lint denied", "OPT"), + opt::multi_s("F", "forbid", "Set lint forbidden", "OPT"), + opt::multi_s( + "", + "cap-lints", + "Set the most restrictive lint level. \ + More restrictive lints are capped at this \ + level", + "LEVEL", + ), + opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), + opt::flag_s("V", "version", "Print version info and exit"), + opt::flag_s("v", "verbose", "Use verbose output"), + ] +} + +/// Returns all rustc command line options, including metadata for +/// each option, such as whether the option is part of the stable +/// long-term interface for rustc. +pub fn rustc_optgroups() -> Vec<RustcOptGroup> { + let mut opts = rustc_short_optgroups(); + opts.extend(vec![ + opt::multi_s( + "", + "extern", + "Specify where an external rust library is located", + "NAME[=PATH]", + ), + opt::opt_s("", "sysroot", "Override the system root", "PATH"), + opt::multi("Z", "", "Set internal debugging options", "FLAG"), + opt::opt_s( + "", + "error-format", + "How errors and other messages are produced", + "human|json|short", + ), + opt::multi_s("", "json", "Configure the JSON output of the compiler", "CONFIG"), + opt::opt_s( + "", + "color", + "Configure coloring of output: + auto = colorize, if output goes to a tty (default); + always = always colorize output; + never = never colorize output", + "auto|always|never", + ), + opt::opt( + "", + "pretty", + "Pretty-print the input instead of compiling; + valid types are: `normal` (un-annotated source), + `expanded` (crates expanded), or + `expanded,identified` (fully parenthesized, AST nodes with IDs).", + "TYPE", + ), + opt::multi_s( + "", + "remap-path-prefix", + "Remap source names in all output (compiler messages and output files)", + "FROM=TO", + ), + ]); + opts +} + +pub fn get_cmd_lint_options( + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) { + let mut lint_opts_with_position = vec![]; + let mut describe_lints = false; + + for &level in &[lint::Allow, lint::Warn, lint::Deny, lint::Forbid] { + for (passed_arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) { + let arg_pos = if let lint::Forbid = level { + // HACK: forbid is always specified last, so it can't be overridden. + // FIXME: remove this once <https://github.com/rust-lang/rust/issues/70819> is + // fixed and `forbid` works as expected. + usize::MAX + } else { + passed_arg_pos + }; + if lint_name == "help" { + describe_lints = true; + } else { + lint_opts_with_position.push((arg_pos, lint_name.replace("-", "_"), level)); + } + } + } + + lint_opts_with_position.sort_by_key(|x| x.0); + let lint_opts = lint_opts_with_position + .iter() + .cloned() + .map(|(_, lint_name, level)| (lint_name, level)) + .collect(); + + let lint_cap = matches.opt_str("cap-lints").map(|cap| { + lint::Level::from_str(&cap) + .unwrap_or_else(|| early_error(error_format, &format!("unknown lint level: `{}`", cap))) + }); + (lint_opts, describe_lints, lint_cap) +} + +/// Parses the `--color` flag. +pub fn parse_color(matches: &getopts::Matches) -> ColorConfig { + match matches.opt_str("color").as_ref().map(|s| &s[..]) { + Some("auto") => ColorConfig::Auto, + Some("always") => ColorConfig::Always, + Some("never") => ColorConfig::Never, + + None => ColorConfig::Auto, + + Some(arg) => early_error( + ErrorOutputType::default(), + &format!( + "argument for `--color` must be auto, \ + always or never (instead was `{}`)", + arg + ), + ), + } +} + +/// Parse the `--json` flag. +/// +/// The first value returned is how to render JSON diagnostics, and the second +/// is whether or not artifact notifications are enabled. +pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) { + let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = + HumanReadableErrorType::Default; + let mut json_color = ColorConfig::Never; + let mut json_artifact_notifications = false; + for option in matches.opt_strs("json") { + // For now conservatively forbid `--color` with `--json` since `--json` + // won't actually be emitting any colors and anything colorized is + // embedded in a diagnostic message anyway. + if matches.opt_str("color").is_some() { + early_error( + ErrorOutputType::default(), + "cannot specify the `--color` option with `--json`", + ); + } + + for sub_option in option.split(',') { + match sub_option { + "diagnostic-short" => json_rendered = HumanReadableErrorType::Short, + "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, + "artifacts" => json_artifact_notifications = true, + s => early_error( + ErrorOutputType::default(), + &format!("unknown `--json` option `{}`", s), + ), + } + } + } + (json_rendered(json_color), json_artifact_notifications) +} + +/// Parses the `--error-format` flag. +pub fn parse_error_format( + matches: &getopts::Matches, + color: ColorConfig, + json_rendered: HumanReadableErrorType, +) -> ErrorOutputType { + // We need the `opts_present` check because the driver will send us Matches + // with only stable options if no unstable options are used. Since error-format + // is unstable, it will not be present. We have to use `opts_present` not + // `opt_present` because the latter will panic. + let error_format = if matches.opts_present(&["error-format".to_owned()]) { + match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { + None | Some("human") => { + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + } + Some("human-annotate-rs") => { + ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color)) + } + Some("json") => ErrorOutputType::Json { pretty: false, json_rendered }, + Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, + Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), + + Some(arg) => early_error( + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)), + &format!( + "argument for `--error-format` must be `human`, `json` or \ + `short` (instead was `{}`)", + arg + ), + ), + } + } else { + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + }; + + match error_format { + ErrorOutputType::Json { .. } => {} + + // Conservatively require that the `--json` argument is coupled with + // `--error-format=json`. This means that `--json` is specified we + // should actually be emitting JSON blobs. + _ if !matches.opt_strs("json").is_empty() => { + early_error( + ErrorOutputType::default(), + "using `--json` requires also using `--error-format=json`", + ); + } + + _ => {} + } + + error_format +} + +fn parse_crate_edition(matches: &getopts::Matches) -> Edition { + let edition = match matches.opt_str("edition") { + Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| { + early_error( + ErrorOutputType::default(), + &format!( + "argument for `--edition` must be one of: \ + {}. (instead was `{}`)", + EDITION_NAME_LIST, arg + ), + ) + }), + None => DEFAULT_EDITION, + }; + + if !edition.is_stable() && !nightly_options::is_nightly_build() { + early_error( + ErrorOutputType::default(), + &format!( + "edition {} is unstable and only \ + available for nightly builds of rustc.", + edition, + ), + ) + } + + edition +} + +fn check_debug_option_stability( + debugging_opts: &DebuggingOptions, + error_format: ErrorOutputType, + json_rendered: HumanReadableErrorType, +) { + if !debugging_opts.unstable_options { + if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format { + early_error( + ErrorOutputType::Json { pretty: false, json_rendered }, + "`--error-format=pretty-json` is unstable", + ); + } + if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = + error_format + { + early_error( + ErrorOutputType::Json { pretty: false, json_rendered }, + "`--error-format=human-annotate-rs` is unstable", + ); + } + } +} + +fn parse_output_types( + debugging_opts: &DebuggingOptions, + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> OutputTypes { + let mut output_types = BTreeMap::new(); + if !debugging_opts.parse_only { + for list in matches.opt_strs("emit") { + for output_type in list.split(',') { + let mut parts = output_type.splitn(2, '='); + let shorthand = parts.next().unwrap(); + let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { + early_error( + error_format, + &format!( + "unknown emission type: `{}` - expected one of: {}", + shorthand, + OutputType::shorthands_display(), + ), + ) + }); + let path = parts.next().map(PathBuf::from); + output_types.insert(output_type, path); + } + } + }; + if output_types.is_empty() { + output_types.insert(OutputType::Exe, None); + } + OutputTypes(output_types) +} + +fn should_override_cgus_and_disable_thinlto( + output_types: &OutputTypes, + matches: &getopts::Matches, + error_format: ErrorOutputType, + mut codegen_units: Option<usize>, +) -> (bool, Option<usize>) { + let mut disable_thinlto = false; + // Issue #30063: if user requests LLVM-related output to one + // particular path, disable codegen-units. + let incompatible: Vec<_> = output_types + .0 + .iter() + .map(|ot_path| ot_path.0) + .filter(|ot| !ot.is_compatible_with_codegen_units_and_single_output_file()) + .map(|ot| ot.shorthand()) + .collect(); + if !incompatible.is_empty() { + match codegen_units { + Some(n) if n > 1 => { + if matches.opt_present("o") { + for ot in &incompatible { + early_warn( + error_format, + &format!( + "`--emit={}` with `-o` incompatible with \ + `-C codegen-units=N` for N > 1", + ot + ), + ); + } + early_warn(error_format, "resetting to default -C codegen-units=1"); + codegen_units = Some(1); + disable_thinlto = true; + } + } + _ => { + codegen_units = Some(1); + disable_thinlto = true; + } + } + } + + if codegen_units == Some(0) { + early_error(error_format, "value for codegen units must be a positive non-zero integer"); + } + + (disable_thinlto, codegen_units) +} + +fn check_thread_count(debugging_opts: &DebuggingOptions, error_format: ErrorOutputType) { + if debugging_opts.threads == 0 { + early_error(error_format, "value for threads must be a positive non-zero integer"); + } + + if debugging_opts.threads > 1 && debugging_opts.fuel.is_some() { + early_error(error_format, "optimization fuel is incompatible with multiple threads"); + } +} + +fn collect_print_requests( + cg: &mut CodegenOptions, + dopts: &mut DebuggingOptions, + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> Vec<PrintRequest> { + let mut prints = Vec::<PrintRequest>::new(); + if cg.target_cpu.as_ref().map_or(false, |s| s == "help") { + prints.push(PrintRequest::TargetCPUs); + cg.target_cpu = None; + }; + if cg.target_feature == "help" { + prints.push(PrintRequest::TargetFeatures); + cg.target_feature = String::new(); + } + + prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s { + "crate-name" => PrintRequest::CrateName, + "file-names" => PrintRequest::FileNames, + "sysroot" => PrintRequest::Sysroot, + "target-libdir" => PrintRequest::TargetLibdir, + "cfg" => PrintRequest::Cfg, + "target-list" => PrintRequest::TargetList, + "target-cpus" => PrintRequest::TargetCPUs, + "target-features" => PrintRequest::TargetFeatures, + "relocation-models" => PrintRequest::RelocationModels, + "code-models" => PrintRequest::CodeModels, + "tls-models" => PrintRequest::TlsModels, + "native-static-libs" => PrintRequest::NativeStaticLibs, + "target-spec-json" => { + if dopts.unstable_options { + PrintRequest::TargetSpec + } else { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to \ + enable the target-spec-json print option", + ); + } + } + req => early_error(error_format, &format!("unknown print request `{}`", req)), + })); + + prints +} + +fn parse_target_triple(matches: &getopts::Matches, error_format: ErrorOutputType) -> TargetTriple { + match matches.opt_str("target") { + Some(target) if target.ends_with(".json") => { + let path = Path::new(&target); + TargetTriple::from_path(&path).unwrap_or_else(|_| { + early_error(error_format, &format!("target file {:?} does not exist", path)) + }) + } + Some(target) => TargetTriple::TargetTriple(target), + _ => TargetTriple::from_triple(host_triple()), + } +} + +fn parse_opt_level( + matches: &getopts::Matches, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> OptLevel { + // The `-O` and `-C opt-level` flags specify the same setting, so we want to be able + // to use them interchangeably. However, because they're technically different flags, + // we need to work out manually which should take precedence if both are supplied (i.e. + // the rightmost flag). We do this by finding the (rightmost) position of both flags and + // comparing them. Note that if a flag is not found, its position will be `None`, which + // always compared less than `Some(_)`. + let max_o = matches.opt_positions("O").into_iter().max(); + let max_c = matches + .opt_strs_pos("C") + .into_iter() + .flat_map( + |(i, s)| { + if let Some("opt-level") = s.splitn(2, '=').next() { Some(i) } else { None } + }, + ) + .max(); + if max_o > max_c { + OptLevel::Default + } else { + match cg.opt_level.as_ref() { + "0" => OptLevel::No, + "1" => OptLevel::Less, + "2" => OptLevel::Default, + "3" => OptLevel::Aggressive, + "s" => OptLevel::Size, + "z" => OptLevel::SizeMin, + arg => { + early_error( + error_format, + &format!( + "optimization level needs to be \ + between 0-3, s or z (instead was `{}`)", + arg + ), + ); + } + } + } +} + +fn select_debuginfo( + matches: &getopts::Matches, + cg: &CodegenOptions, + error_format: ErrorOutputType, +) -> DebugInfo { + let max_g = matches.opt_positions("g").into_iter().max(); + let max_c = matches + .opt_strs_pos("C") + .into_iter() + .flat_map( + |(i, s)| { + if let Some("debuginfo") = s.splitn(2, '=').next() { Some(i) } else { None } + }, + ) + .max(); + if max_g > max_c { + DebugInfo::Full + } else { + match cg.debuginfo { + 0 => DebugInfo::None, + 1 => DebugInfo::Limited, + 2 => DebugInfo::Full, + arg => { + early_error( + error_format, + &format!( + "debug info level needs to be between \ + 0-2 (instead was `{}`)", + arg + ), + ); + } + } + } +} + +fn parse_libs( + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> Vec<(String, Option<String>, NativeLibKind)> { + matches + .opt_strs("l") + .into_iter() + .map(|s| { + // Parse string of the form "[KIND=]lib[:new_name]", + // where KIND is one of "dylib", "framework", "static". + let mut parts = s.splitn(2, '='); + let kind = parts.next().unwrap(); + let (name, kind) = match (parts.next(), kind) { + (None, name) => (name, NativeLibKind::Unspecified), + (Some(name), "dylib") => (name, NativeLibKind::Dylib), + (Some(name), "framework") => (name, NativeLibKind::Framework), + (Some(name), "static") => (name, NativeLibKind::StaticBundle), + (Some(name), "static-nobundle") => (name, NativeLibKind::StaticNoBundle), + (_, s) => { + early_error( + error_format, + &format!( + "unknown library kind `{}`, expected \ + one of dylib, framework, or static", + s + ), + ); + } + }; + if kind == NativeLibKind::StaticNoBundle && !nightly_options::is_nightly_build() { + early_error( + error_format, + "the library kind 'static-nobundle' is only \ + accepted on the nightly compiler", + ); + } + let mut name_parts = name.splitn(2, ':'); + let name = name_parts.next().unwrap(); + let new_name = name_parts.next(); + (name.to_owned(), new_name.map(|n| n.to_owned()), kind) + }) + .collect() +} + +fn parse_borrowck_mode(dopts: &DebuggingOptions, error_format: ErrorOutputType) -> BorrowckMode { + match dopts.borrowck.as_ref() { + "migrate" => BorrowckMode::Migrate, + "mir" => BorrowckMode::Mir, + m => early_error(error_format, &format!("unknown borrowck mode `{}`", m)), + } +} + +pub fn parse_externs( + matches: &getopts::Matches, + debugging_opts: &DebuggingOptions, + error_format: ErrorOutputType, +) -> Externs { + let is_unstable_enabled = debugging_opts.unstable_options; + let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new(); + for arg in matches.opt_strs("extern") { + let mut parts = arg.splitn(2, '='); + let name = parts + .next() + .unwrap_or_else(|| early_error(error_format, "--extern value must not be empty")); + let path = parts.next().map(|s| s.to_string()); + + let mut name_parts = name.splitn(2, ':'); + let first_part = name_parts.next(); + let second_part = name_parts.next(); + let (options, name) = match (first_part, second_part) { + (Some(opts), Some(name)) => (Some(opts), name), + (Some(name), None) => (None, name), + (None, None) => early_error(error_format, "--extern name must not be empty"), + _ => unreachable!(), + }; + + let entry = externs.entry(name.to_owned()); + + use std::collections::btree_map::Entry; + + let entry = if let Some(path) = path { + // --extern prelude_name=some_file.rlib + match entry { + Entry::Vacant(vacant) => { + let files = BTreeSet::from_iter(iter::once(path)); + vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files))) + } + Entry::Occupied(occupied) => { + let ext_ent = occupied.into_mut(); + match ext_ent { + ExternEntry { location: ExternLocation::ExactPaths(files), .. } => { + files.insert(path); + } + ExternEntry { + location: location @ ExternLocation::FoundInLibrarySearchDirectories, + .. + } => { + // Exact paths take precedence over search directories. + let files = BTreeSet::from_iter(iter::once(path)); + *location = ExternLocation::ExactPaths(files); + } + } + ext_ent + } + } + } else { + // --extern prelude_name + match entry { + Entry::Vacant(vacant) => { + vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories)) + } + Entry::Occupied(occupied) => { + // Ignore if already specified. + occupied.into_mut() + } + } + }; + + let mut is_private_dep = false; + let mut add_prelude = true; + if let Some(opts) = options { + if !is_unstable_enabled { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to \ + enable `--extern options", + ); + } + for opt in opts.split(',') { + match opt { + "priv" => is_private_dep = true, + "noprelude" => { + if let ExternLocation::ExactPaths(_) = &entry.location { + add_prelude = false; + } else { + early_error( + error_format, + "the `noprelude` --extern option requires a file path", + ); + } + } + _ => early_error(error_format, &format!("unknown --extern option `{}`", opt)), + } + } + } + + // Crates start out being not private, and go to being private `priv` + // is specified. + entry.is_private_dep |= is_private_dep; + // If any flag is missing `noprelude`, then add to the prelude. + entry.add_prelude |= add_prelude; + } + Externs(externs) +} + +fn parse_remap_path_prefix( + matches: &getopts::Matches, + error_format: ErrorOutputType, +) -> Vec<(PathBuf, PathBuf)> { + matches + .opt_strs("remap-path-prefix") + .into_iter() + .map(|remap| { + let mut parts = remap.rsplitn(2, '='); // reverse iterator + let to = parts.next(); + let from = parts.next(); + match (from, to) { + (Some(from), Some(to)) => (PathBuf::from(from), PathBuf::from(to)), + _ => early_error( + error_format, + "--remap-path-prefix must contain '=' between FROM and TO", + ), + } + }) + .collect() +} + +pub fn build_session_options(matches: &getopts::Matches) -> Options { + let color = parse_color(matches); + + let edition = parse_crate_edition(matches); + + let (json_rendered, json_artifact_notifications) = parse_json(matches); + + let error_format = parse_error_format(matches, color, json_rendered); + + let unparsed_crate_types = matches.opt_strs("crate-type"); + let crate_types = parse_crate_types_from_list(unparsed_crate_types) + .unwrap_or_else(|e| early_error(error_format, &e[..])); + + let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); + + let mut debugging_opts = build_debugging_options(matches, error_format); + check_debug_option_stability(&debugging_opts, error_format, json_rendered); + + let output_types = parse_output_types(&debugging_opts, matches, error_format); + + let mut cg = build_codegen_options(matches, error_format); + let (disable_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto( + &output_types, + matches, + error_format, + cg.codegen_units, + ); + + check_thread_count(&debugging_opts, error_format); + + let incremental = cg.incremental.as_ref().map(PathBuf::from); + + if debugging_opts.profile && incremental.is_some() { + early_error( + error_format, + "can't instrument with gcov profiling when compiling incrementally", + ); + } + if debugging_opts.profile { + match codegen_units { + Some(1) => {} + None => codegen_units = Some(1), + Some(_) => early_error( + error_format, + "can't instrument with gcov profiling with multiple codegen units", + ), + } + } + + if cg.profile_generate.enabled() && cg.profile_use.is_some() { + early_error( + error_format, + "options `-C profile-generate` and `-C profile-use` are exclusive", + ); + } + + if debugging_opts.instrument_coverage { + if cg.profile_generate.enabled() || cg.profile_use.is_some() { + early_error( + error_format, + "option `-Z instrument-coverage` is not compatible with either `-C profile-use` \ + or `-C profile-generate`", + ); + } + + // `-Z instrument-coverage` implies: + // * `-Z 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. + // * `-C link-dead-code` - so unexecuted code is still counted as zero, rather than be + // optimized out. Note that instrumenting dead code can be explicitly disabled with: + // `-Z instrument-coverage -C link-dead-code=no`. + debugging_opts.symbol_mangling_version = SymbolManglingVersion::V0; + if cg.link_dead_code == None { + // FIXME(richkadel): Investigate if the `instrument-coverage` implementation can + // inject ["zero counters"](https://llvm.org/docs/CoverageMappingFormat.html#counter) + // in the coverage map when "dead code" is removed, rather than forcing `link-dead-code`. + cg.link_dead_code = Some(true); + } + } + + if !cg.embed_bitcode { + match cg.lto { + LtoCli::No | LtoCli::Unspecified => {} + LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => early_error( + error_format, + "options `-C embed-bitcode=no` and `-C lto` are incompatible", + ), + } + } + + let prints = collect_print_requests(&mut cg, &mut debugging_opts, matches, error_format); + + let cg = cg; + + let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); + let target_triple = parse_target_triple(matches, error_format); + let opt_level = parse_opt_level(matches, &cg, error_format); + // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able + // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) + // for more details. + let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No); + let debuginfo = select_debuginfo(matches, &cg, error_format); + + let mut search_paths = vec![]; + for s in &matches.opt_strs("L") { + search_paths.push(SearchPath::from_cli_opt(&s[..], error_format)); + } + + let libs = parse_libs(matches, error_format); + + let test = matches.opt_present("test"); + + let borrowck_mode = parse_borrowck_mode(&debugging_opts, error_format); + + if !cg.remark.is_empty() && debuginfo == DebugInfo::None { + early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations"); + } + + let externs = parse_externs(matches, &debugging_opts, error_format); + + let crate_name = matches.opt_str("crate-name"); + + let remap_path_prefix = parse_remap_path_prefix(matches, error_format); + + let pretty = parse_pretty(matches, &debugging_opts, error_format); + + Options { + crate_types, + optimize: opt_level, + debuginfo, + lint_opts, + lint_cap, + describe_lints, + output_types, + search_paths, + maybe_sysroot: sysroot_opt, + target_triple, + test, + incremental, + debugging_opts, + prints, + borrowck_mode, + cg, + error_format, + externs, + crate_name, + alt_std_name: None, + libs, + unstable_features: UnstableFeatures::from_environment(), + debug_assertions, + actually_rustdoc: false, + cli_forced_codegen_units: codegen_units, + cli_forced_thinlto_off: disable_thinlto, + remap_path_prefix, + edition, + json_artifact_notifications, + pretty, + } +} + +fn parse_pretty( + matches: &getopts::Matches, + debugging_opts: &DebuggingOptions, + efmt: ErrorOutputType, +) -> Option<PpMode> { + let pretty = if debugging_opts.unstable_options { + matches.opt_default("pretty", "normal").map(|a| { + // stable pretty-print variants only + parse_pretty_inner(efmt, &a, false) + }) + } else { + None + }; + + return if pretty.is_none() { + debugging_opts.unpretty.as_ref().map(|a| { + // extended with unstable pretty-print variants + parse_pretty_inner(efmt, &a, true) + }) + } else { + pretty + }; + + fn parse_pretty_inner(efmt: ErrorOutputType, name: &str, extended: bool) -> PpMode { + use PpMode::*; + use PpSourceMode::*; + let first = match (name, extended) { + ("normal", _) => PpmSource(PpmNormal), + ("identified", _) => PpmSource(PpmIdentified), + ("everybody_loops", true) => PpmSource(PpmEveryBodyLoops), + ("expanded", _) => PpmSource(PpmExpanded), + ("expanded,identified", _) => PpmSource(PpmExpandedIdentified), + ("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene), + ("hir", true) => PpmHir(PpmNormal), + ("hir,identified", true) => PpmHir(PpmIdentified), + ("hir,typed", true) => PpmHir(PpmTyped), + ("hir-tree", true) => PpmHirTree(PpmNormal), + ("mir", true) => PpmMir, + ("mir-cfg", true) => PpmMirCFG, + _ => { + if extended { + early_error( + efmt, + &format!( + "argument to `unpretty` must be one of `normal`, \ + `expanded`, `identified`, `expanded,identified`, \ + `expanded,hygiene`, `everybody_loops`, \ + `hir`, `hir,identified`, `hir,typed`, `hir-tree`, \ + `mir` or `mir-cfg`; got {}", + name + ), + ); + } else { + early_error( + efmt, + &format!( + "argument to `pretty` must be one of `normal`, \ + `expanded`, `identified`, or `expanded,identified`; got {}", + name + ), + ); + } + } + }; + tracing::debug!("got unpretty option: {:?}", first); + first + } +} + +pub fn make_crate_type_option() -> RustcOptGroup { + opt::multi_s( + "", + "crate-type", + "Comma separated list of types of crates + for the compiler to emit", + "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]", + ) +} + +pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> { + let mut crate_types: Vec<CrateType> = Vec::new(); + for unparsed_crate_type in &list_list { + for part in unparsed_crate_type.split(',') { + let new_part = match part { + "lib" => default_lib_output(), + "rlib" => CrateType::Rlib, + "staticlib" => CrateType::Staticlib, + "dylib" => CrateType::Dylib, + "cdylib" => CrateType::Cdylib, + "bin" => CrateType::Executable, + "proc-macro" => CrateType::ProcMacro, + _ => return Err(format!("unknown crate type: `{}`", part)), + }; + if !crate_types.contains(&new_part) { + crate_types.push(new_part) + } + } + } + + Ok(crate_types) +} + +pub mod nightly_options { + use super::{ErrorOutputType, OptionStability, RustcOptGroup}; + use crate::early_error; + use rustc_feature::UnstableFeatures; + + pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool { + is_nightly_build() && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options") + } + + pub fn is_nightly_build() -> bool { + UnstableFeatures::from_environment().is_nightly_build() + } + + pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) { + let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options"); + let really_allows_unstable_options = + UnstableFeatures::from_environment().is_nightly_build(); + + for opt in flags.iter() { + if opt.stability == OptionStability::Stable { + continue; + } + if !matches.opt_present(opt.name) { + continue; + } + if opt.name != "Z" && !has_z_unstable_option { + early_error( + ErrorOutputType::default(), + &format!( + "the `-Z unstable-options` flag must also be passed to enable \ + the flag `{}`", + opt.name + ), + ); + } + if really_allows_unstable_options { + continue; + } + match opt.stability { + OptionStability::Unstable => { + let msg = format!( + "the option `{}` is only accepted on the \ + nightly compiler", + opt.name + ); + early_error(ErrorOutputType::default(), &msg); + } + OptionStability::Stable => {} + } + } + } +} + +impl fmt::Display for CrateType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + CrateType::Executable => "bin".fmt(f), + CrateType::Dylib => "dylib".fmt(f), + CrateType::Rlib => "rlib".fmt(f), + CrateType::Staticlib => "staticlib".fmt(f), + CrateType::Cdylib => "cdylib".fmt(f), + CrateType::ProcMacro => "proc-macro".fmt(f), + } + } +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum PpSourceMode { + PpmNormal, + PpmEveryBodyLoops, + PpmExpanded, + PpmIdentified, + PpmExpandedIdentified, + PpmExpandedHygiene, + PpmTyped, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum PpMode { + PpmSource(PpSourceMode), + PpmHir(PpSourceMode), + PpmHirTree(PpSourceMode), + PpmMir, + PpmMirCFG, +} + +impl PpMode { + pub fn needs_ast_map(&self) -> bool { + use PpMode::*; + use PpSourceMode::*; + match *self { + PpmSource(PpmNormal | PpmIdentified) => false, + + PpmSource( + PpmExpanded | PpmEveryBodyLoops | PpmExpandedIdentified | PpmExpandedHygiene, + ) + | PpmHir(_) + | PpmHirTree(_) + | PpmMir + | PpmMirCFG => true, + PpmSource(PpmTyped) => panic!("invalid state"), + } + } + + pub fn needs_analysis(&self) -> bool { + use PpMode::*; + match *self { + PpmMir | PpmMirCFG => true, + _ => false, + } + } +} + +/// 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. +crate mod dep_tracking { + use super::{ + CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, + OutputTypes, Passes, SanitizerSet, SourceFileHashAlgorithm, SwitchWithOptPath, + SymbolManglingVersion, + }; + use crate::lint; + use crate::utils::NativeLibKind; + use rustc_feature::UnstableFeatures; + use rustc_span::edition::Edition; + use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel}; + use rustc_target::spec::{RelroLevel, TargetTriple, TlsModel}; + use std::collections::hash_map::DefaultHasher; + use std::collections::BTreeMap; + use std::hash::Hash; + use std::path::PathBuf; + + pub trait DepTrackingHash { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType); + } + + macro_rules! impl_dep_tracking_hash_via_hash { + ($t:ty) => { + impl DepTrackingHash for $t { + fn hash(&self, hasher: &mut DefaultHasher, _: ErrorOutputType) { + Hash::hash(self, hasher); + } + } + }; + } + + macro_rules! impl_dep_tracking_hash_for_sortable_vec_of { + ($t:ty) => { + impl DepTrackingHash for Vec<$t> { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { + let mut elems: Vec<&$t> = self.iter().collect(); + elems.sort(); + Hash::hash(&elems.len(), hasher); + for (index, elem) in elems.iter().enumerate() { + Hash::hash(&index, hasher); + DepTrackingHash::hash(*elem, hasher, error_format); + } + } + } + }; + } + + impl_dep_tracking_hash_via_hash!(bool); + impl_dep_tracking_hash_via_hash!(usize); + impl_dep_tracking_hash_via_hash!(u64); + impl_dep_tracking_hash_via_hash!(String); + impl_dep_tracking_hash_via_hash!(PathBuf); + impl_dep_tracking_hash_via_hash!(lint::Level); + impl_dep_tracking_hash_via_hash!(Option<bool>); + impl_dep_tracking_hash_via_hash!(Option<usize>); + impl_dep_tracking_hash_via_hash!(Option<String>); + impl_dep_tracking_hash_via_hash!(Option<(String, u64)>); + impl_dep_tracking_hash_via_hash!(Option<Vec<String>>); + impl_dep_tracking_hash_via_hash!(Option<MergeFunctions>); + impl_dep_tracking_hash_via_hash!(Option<RelocModel>); + impl_dep_tracking_hash_via_hash!(Option<CodeModel>); + impl_dep_tracking_hash_via_hash!(Option<TlsModel>); + impl_dep_tracking_hash_via_hash!(Option<PanicStrategy>); + impl_dep_tracking_hash_via_hash!(Option<RelroLevel>); + impl_dep_tracking_hash_via_hash!(Option<lint::Level>); + impl_dep_tracking_hash_via_hash!(Option<PathBuf>); + impl_dep_tracking_hash_via_hash!(CrateType); + impl_dep_tracking_hash_via_hash!(MergeFunctions); + impl_dep_tracking_hash_via_hash!(PanicStrategy); + impl_dep_tracking_hash_via_hash!(RelroLevel); + impl_dep_tracking_hash_via_hash!(Passes); + impl_dep_tracking_hash_via_hash!(OptLevel); + impl_dep_tracking_hash_via_hash!(LtoCli); + impl_dep_tracking_hash_via_hash!(DebugInfo); + impl_dep_tracking_hash_via_hash!(UnstableFeatures); + impl_dep_tracking_hash_via_hash!(OutputTypes); + impl_dep_tracking_hash_via_hash!(NativeLibKind); + impl_dep_tracking_hash_via_hash!(SanitizerSet); + impl_dep_tracking_hash_via_hash!(CFGuard); + impl_dep_tracking_hash_via_hash!(TargetTriple); + impl_dep_tracking_hash_via_hash!(Edition); + impl_dep_tracking_hash_via_hash!(LinkerPluginLto); + impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); + impl_dep_tracking_hash_via_hash!(SymbolManglingVersion); + impl_dep_tracking_hash_via_hash!(Option<SourceFileHashAlgorithm>); + + impl_dep_tracking_hash_for_sortable_vec_of!(String); + impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); + impl_dep_tracking_hash_for_sortable_vec_of!(CrateType); + impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level)); + impl_dep_tracking_hash_for_sortable_vec_of!((String, Option<String>, NativeLibKind)); + impl_dep_tracking_hash_for_sortable_vec_of!((String, u64)); + + impl<T1, T2> DepTrackingHash for (T1, T2) + where + T1: DepTrackingHash, + T2: DepTrackingHash, + { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { + Hash::hash(&0, hasher); + DepTrackingHash::hash(&self.0, hasher, error_format); + Hash::hash(&1, hasher); + DepTrackingHash::hash(&self.1, hasher, error_format); + } + } + + 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) { + Hash::hash(&0, hasher); + DepTrackingHash::hash(&self.0, hasher, error_format); + Hash::hash(&1, hasher); + DepTrackingHash::hash(&self.1, hasher, error_format); + Hash::hash(&2, hasher); + DepTrackingHash::hash(&self.2, hasher, error_format); + } + } + + // This is a stable hash because BTreeMap is a sorted container + pub fn stable_hash( + sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>, + hasher: &mut DefaultHasher, + error_format: ErrorOutputType, + ) { + 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); + } + } +} diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs new file mode 100644 index 00000000000..284fca652ec --- /dev/null +++ b/compiler/rustc_session/src/filesearch.rs @@ -0,0 +1,171 @@ +#![allow(non_camel_case_types)] + +pub use self::FileMatch::*; + +use std::borrow::Cow; +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; + +use crate::search_paths::{PathKind, SearchPath, SearchPathFile}; +use rustc_fs_util::fix_windows_verbatim_for_gcc; +use tracing::debug; + +#[derive(Copy, Clone)] +pub enum FileMatch { + FileMatches, + FileDoesntMatch, +} + +// A module for searching for libraries + +#[derive(Clone)] +pub struct FileSearch<'a> { + sysroot: &'a Path, + triple: &'a str, + search_paths: &'a [SearchPath], + tlib_path: &'a SearchPath, + kind: PathKind, +} + +impl<'a> FileSearch<'a> { + pub fn search_paths(&self) -> impl Iterator<Item = &'a SearchPath> { + let kind = self.kind; + self.search_paths + .iter() + .filter(move |sp| sp.kind.matches(kind)) + .chain(std::iter::once(self.tlib_path)) + } + + pub fn get_lib_path(&self) -> PathBuf { + make_target_lib_path(self.sysroot, self.triple) + } + + pub fn get_self_contained_lib_path(&self) -> PathBuf { + self.get_lib_path().join("self-contained") + } + + pub fn search<F>(&self, mut pick: F) + where + F: FnMut(&SearchPathFile, PathKind) -> FileMatch, + { + for search_path in self.search_paths() { + debug!("searching {}", search_path.dir.display()); + fn is_rlib(spf: &SearchPathFile) -> bool { + if let Some(f) = &spf.file_name_str { f.ends_with(".rlib") } else { false } + } + // Reading metadata out of rlibs is faster, and if we find both + // an rlib and a dylib we only read one of the files of + // metadata, so in the name of speed, bring all rlib files to + // the front of the search list. + let files1 = search_path.files.iter().filter(|spf| is_rlib(&spf)); + let files2 = search_path.files.iter().filter(|spf| !is_rlib(&spf)); + for spf in files1.chain(files2) { + debug!("testing {}", spf.path.display()); + let maybe_picked = pick(spf, search_path.kind); + match maybe_picked { + FileMatches => { + debug!("picked {}", spf.path.display()); + } + FileDoesntMatch => { + debug!("rejected {}", spf.path.display()); + } + } + } + } + } + + pub fn new( + sysroot: &'a Path, + triple: &'a str, + search_paths: &'a Vec<SearchPath>, + tlib_path: &'a SearchPath, + kind: PathKind, + ) -> FileSearch<'a> { + debug!("using sysroot = {}, triple = {}", sysroot.display(), triple); + FileSearch { sysroot, triple, search_paths, tlib_path, kind } + } + + // Returns just the directories within the search paths. + pub fn search_path_dirs(&self) -> Vec<PathBuf> { + self.search_paths().map(|sp| sp.dir.to_path_buf()).collect() + } + + // Returns a list of directories where target-specific tool binaries are located. + pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec<PathBuf> { + let mut p = PathBuf::from(self.sysroot); + p.push(find_libdir(self.sysroot).as_ref()); + p.push(RUST_LIB_DIR); + p.push(&self.triple); + p.push("bin"); + if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] } + } +} + +pub fn relative_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { + let mut p = PathBuf::from(find_libdir(sysroot).as_ref()); + assert!(p.is_relative()); + p.push(RUST_LIB_DIR); + p.push(target_triple); + p.push("lib"); + p +} + +pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { + sysroot.join(&relative_target_lib_path(sysroot, target_triple)) +} + +pub fn get_or_default_sysroot() -> PathBuf { + // Follow symlinks. If the resolved path is relative, make it absolute. + fn canonicalize(path: PathBuf) -> PathBuf { + let path = fs::canonicalize(&path).unwrap_or(path); + // See comments on this target function, but the gist is that + // gcc chokes on verbatim paths which fs::canonicalize generates + // so we try to avoid those kinds of paths. + fix_windows_verbatim_for_gcc(&path) + } + + match env::current_exe() { + Ok(exe) => { + let mut p = canonicalize(exe); + p.pop(); + p.pop(); + p + } + Err(e) => panic!("failed to get current_exe: {}", e), + } +} + +// The name of the directory rustc expects libraries to be located. +fn find_libdir(sysroot: &Path) -> Cow<'static, str> { + // FIXME: This is a quick hack to make the rustc binary able to locate + // Rust libraries in Linux environments where libraries might be installed + // to lib64/lib32. This would be more foolproof by basing the sysroot off + // of the directory where `librustc_driver` is located, rather than + // where the rustc binary is. + // If --libdir is set during configuration to the value other than + // "lib" (i.e., non-default), this value is used (see issue #16552). + + #[cfg(target_pointer_width = "64")] + const PRIMARY_LIB_DIR: &str = "lib64"; + + #[cfg(target_pointer_width = "32")] + const PRIMARY_LIB_DIR: &str = "lib32"; + + const SECONDARY_LIB_DIR: &str = "lib"; + + match option_env!("CFG_LIBDIR_RELATIVE") { + Some(libdir) if libdir != "lib" => libdir.into(), + _ => { + if sysroot.join(PRIMARY_LIB_DIR).join(RUST_LIB_DIR).exists() { + PRIMARY_LIB_DIR.into() + } else { + SECONDARY_LIB_DIR.into() + } + } + } +} + +// The name of rustc's own place to organize libraries. +// Used to be "rustc", now the default is "rustlib" +const RUST_LIB_DIR: &str = "rustlib"; diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs new file mode 100644 index 00000000000..c2ea141a06f --- /dev/null +++ b/compiler/rustc_session/src/lib.rs @@ -0,0 +1,27 @@ +#![feature(crate_visibility_modifier)] +#![feature(or_patterns)] + +#[macro_use] +extern crate bitflags; +#[macro_use] +extern crate rustc_macros; + +pub mod cgu_reuse_tracker; +pub mod utils; +#[macro_use] +pub mod lint; +pub mod parse; + +mod code_stats; +#[macro_use] +pub mod config; +pub mod filesearch; +mod options; +pub mod search_paths; + +mod session; +pub use session::*; + +pub mod output; + +pub use getopts; diff --git a/compiler/rustc_session/src/lint.rs b/compiler/rustc_session/src/lint.rs new file mode 100644 index 00000000000..0dcbee08abe --- /dev/null +++ b/compiler/rustc_session/src/lint.rs @@ -0,0 +1,425 @@ +pub use self::Level::*; +use rustc_ast::node_id::{NodeId, NodeMap}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; +use rustc_errors::{pluralize, Applicability, DiagnosticBuilder}; +use rustc_span::edition::Edition; +use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol}; + +pub mod builtin; + +/// Setting for how to handle a lint. +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +pub enum Level { + Allow, + Warn, + Deny, + Forbid, +} + +rustc_data_structures::impl_stable_hash_via_hash!(Level); + +impl Level { + /// Converts a level to a lower-case string. + pub fn as_str(self) -> &'static str { + match self { + Level::Allow => "allow", + Level::Warn => "warn", + Level::Deny => "deny", + Level::Forbid => "forbid", + } + } + + /// Converts a lower-case string to a level. + pub fn from_str(x: &str) -> Option<Level> { + match x { + "allow" => Some(Level::Allow), + "warn" => Some(Level::Warn), + "deny" => Some(Level::Deny), + "forbid" => Some(Level::Forbid), + _ => None, + } + } + + /// Converts a symbol to a level. + pub fn from_symbol(x: Symbol) -> Option<Level> { + match x { + sym::allow => Some(Level::Allow), + sym::warn => Some(Level::Warn), + sym::deny => Some(Level::Deny), + sym::forbid => Some(Level::Forbid), + _ => None, + } + } +} + +/// Specification of a single lint. +#[derive(Copy, Clone, Debug)] +pub struct Lint { + /// A string identifier for the lint. + /// + /// This identifies the lint in attributes and in command-line arguments. + /// In those contexts it is always lowercase, but this field is compared + /// in a way which is case-insensitive for ASCII characters. This allows + /// `declare_lint!()` invocations to follow the convention of upper-case + /// statics without repeating the name. + /// + /// The name is written with underscores, e.g., "unused_imports". + /// On the command line, underscores become dashes. + pub name: &'static str, + + /// Default level for the lint. + pub default_level: Level, + + /// Description of the lint or the issue it detects. + /// + /// e.g., "imports that are never used" + pub desc: &'static str, + + /// Starting at the given edition, default to the given lint level. If this is `None`, then use + /// `default_level`. + pub edition_lint_opts: Option<(Edition, Level)>, + + /// `true` if this lint is reported even inside expansions of external macros. + pub report_in_external_macro: bool, + + pub future_incompatible: Option<FutureIncompatibleInfo>, + + pub is_plugin: bool, + + /// `Some` if this lint is feature gated, otherwise `None`. + pub feature_gate: Option<Symbol>, + + pub crate_level_only: bool, +} + +/// Extra information for a future incompatibility lint. +#[derive(Copy, Clone, Debug)] +pub struct FutureIncompatibleInfo { + /// e.g., a URL for an issue/PR/RFC or error code + pub reference: &'static str, + /// If this is an edition fixing lint, the edition in which + /// this lint becomes obsolete + pub edition: Option<Edition>, +} + +impl Lint { + pub const fn default_fields_for_macro() -> Self { + Lint { + name: "", + default_level: Level::Forbid, + desc: "", + edition_lint_opts: None, + is_plugin: false, + report_in_external_macro: false, + future_incompatible: None, + feature_gate: None, + crate_level_only: false, + } + } + + /// Gets the lint's name, with ASCII letters converted to lowercase. + pub fn name_lower(&self) -> String { + self.name.to_ascii_lowercase() + } + + pub fn default_level(&self, edition: Edition) -> Level { + self.edition_lint_opts + .filter(|(e, _)| *e <= edition) + .map(|(_, l)| l) + .unwrap_or(self.default_level) + } +} + +/// Identifies a lint known to the compiler. +#[derive(Clone, Copy, Debug)] +pub struct LintId { + // Identity is based on pointer equality of this field. + pub lint: &'static Lint, +} + +impl PartialEq for LintId { + fn eq(&self, other: &LintId) -> bool { + std::ptr::eq(self.lint, other.lint) + } +} + +impl Eq for LintId {} + +impl std::hash::Hash for LintId { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + let ptr = self.lint as *const Lint; + ptr.hash(state); + } +} + +impl LintId { + /// Gets the `LintId` for a `Lint`. + pub fn of(lint: &'static Lint) -> LintId { + LintId { lint } + } + + pub fn lint_name_raw(&self) -> &'static str { + self.lint.name + } + + /// Gets the name of the lint. + pub fn to_string(&self) -> String { + self.lint.name_lower() + } +} + +impl<HCX> HashStable<HCX> for LintId { + #[inline] + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { + self.lint_name_raw().hash_stable(hcx, hasher); + } +} + +impl<HCX> ToStableHashKey<HCX> for LintId { + type KeyType = &'static str; + + #[inline] + fn to_stable_hash_key(&self, _: &HCX) -> &'static str { + self.lint_name_raw() + } +} + +// This could be a closure, but then implementing derive trait +// becomes hacky (and it gets allocated). +#[derive(PartialEq)] +pub enum BuiltinLintDiagnostics { + Normal, + BareTraitObject(Span, /* is_global */ bool), + AbsPathWithModule(Span), + ProcMacroDeriveResolutionFallback(Span), + MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), + ElidedLifetimesInPaths(usize, Span, bool, Span, String), + UnknownCrateTypes(Span, String, String), + UnusedImports(String, Vec<(Span, String)>), + RedundantImport(Vec<(Span, bool)>, Ident), + DeprecatedMacro(Option<Symbol>, Span), + UnusedDocComment(Span), +} + +/// Lints that are buffered up early on in the `Session` before the +/// `LintLevels` is calculated. +#[derive(PartialEq)] +pub struct BufferedEarlyLint { + /// The span of code that we are linting on. + pub span: MultiSpan, + + /// The lint message. + pub msg: String, + + /// The `NodeId` of the AST node that generated the lint. + pub node_id: NodeId, + + /// A lint Id that can be passed to + /// `rustc_lint::early::EarlyContextAndPass::check_id`. + pub lint_id: LintId, + + /// Customization of the `DiagnosticBuilder<'_>` for the lint. + pub diagnostic: BuiltinLintDiagnostics, +} + +#[derive(Default)] +pub struct LintBuffer { + pub map: NodeMap<Vec<BufferedEarlyLint>>, +} + +impl LintBuffer { + pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) { + let arr = self.map.entry(early_lint.node_id).or_default(); + if !arr.contains(&early_lint) { + arr.push(early_lint); + } + } + + pub fn add_lint( + &mut self, + lint: &'static Lint, + node_id: NodeId, + span: MultiSpan, + msg: &str, + diagnostic: BuiltinLintDiagnostics, + ) { + let lint_id = LintId::of(lint); + let msg = msg.to_string(); + self.add_early_lint(BufferedEarlyLint { lint_id, node_id, span, msg, diagnostic }); + } + + pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> { + self.map.remove(&id).unwrap_or_default() + } + + pub fn buffer_lint( + &mut self, + lint: &'static Lint, + id: NodeId, + sp: impl Into<MultiSpan>, + msg: &str, + ) { + self.add_lint(lint, id, sp.into(), msg, BuiltinLintDiagnostics::Normal) + } + + pub fn buffer_lint_with_diagnostic( + &mut self, + lint: &'static Lint, + id: NodeId, + sp: impl Into<MultiSpan>, + msg: &str, + diagnostic: BuiltinLintDiagnostics, + ) { + self.add_lint(lint, id, sp.into(), msg, diagnostic) + } +} + +/// Declares a static item of type `&'static Lint`. +#[macro_export] +macro_rules! declare_lint { + ($vis: vis $NAME: ident, $Level: ident, $desc: expr) => ( + $crate::declare_lint!( + $vis $NAME, $Level, $desc, + ); + ); + ($vis: vis $NAME: ident, $Level: ident, $desc: expr, + $(@future_incompatible = $fi:expr;)? + $(@feature_gate = $gate:expr;)? + $($v:ident),*) => ( + $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { + name: stringify!($NAME), + default_level: $crate::lint::$Level, + desc: $desc, + edition_lint_opts: None, + is_plugin: false, + $($v: true,)* + $(future_incompatible: Some($fi),)* + $(feature_gate: Some($gate),)* + ..$crate::lint::Lint::default_fields_for_macro() + }; + ); + ($vis: vis $NAME: ident, $Level: ident, $desc: expr, + $lint_edition: expr => $edition_level: ident + ) => ( + $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { + name: stringify!($NAME), + default_level: $crate::lint::$Level, + desc: $desc, + edition_lint_opts: Some(($lint_edition, $crate::lint::Level::$edition_level)), + report_in_external_macro: false, + is_plugin: false, + }; + ); +} + +#[macro_export] +macro_rules! declare_tool_lint { + ( + $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level: ident, $desc: expr + ) => ( + $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false} + ); + ( + $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, + report_in_external_macro: $rep:expr + ) => ( + $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep} + ); + ( + $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, + $external:expr + ) => ( + $(#[$attr])* + $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { + name: &concat!(stringify!($tool), "::", stringify!($NAME)), + default_level: $crate::lint::$Level, + desc: $desc, + edition_lint_opts: None, + report_in_external_macro: $external, + future_incompatible: None, + is_plugin: true, + feature_gate: None, + crate_level_only: false, + }; + ); +} + +/// Declares a static `LintArray` and return it as an expression. +#[macro_export] +macro_rules! lint_array { + ($( $lint:expr ),* ,) => { lint_array!( $($lint),* ) }; + ($( $lint:expr ),*) => {{ + vec![$($lint),*] + }} +} + +pub type LintArray = Vec<&'static Lint>; + +pub trait LintPass { + fn name(&self) -> &'static str; +} + +/// Implements `LintPass for $ty` with the given list of `Lint` statics. +#[macro_export] +macro_rules! impl_lint_pass { + ($ty:ty => [$($lint:expr),* $(,)?]) => { + impl $crate::lint::LintPass for $ty { + fn name(&self) -> &'static str { stringify!($ty) } + } + impl $ty { + pub fn get_lints() -> $crate::lint::LintArray { $crate::lint_array!($($lint),*) } + } + }; +} + +/// Declares a type named `$name` which implements `LintPass`. +/// To the right of `=>` a comma separated list of `Lint` statics is given. +#[macro_export] +macro_rules! declare_lint_pass { + ($(#[$m:meta])* $name:ident => [$($lint:expr),* $(,)?]) => { + $(#[$m])* #[derive(Copy, Clone)] pub struct $name; + $crate::impl_lint_pass!($name => [$($lint),*]); + }; +} + +pub fn add_elided_lifetime_in_path_suggestion( + sess: &crate::Session, + db: &mut DiagnosticBuilder<'_>, + n: usize, + path_span: Span, + incl_angl_brckt: bool, + insertion_span: Span, + anon_lts: String, +) { + let (replace_span, suggestion) = if incl_angl_brckt { + (insertion_span, anon_lts) + } else { + // When possible, prefer a suggestion that replaces the whole + // `Path<T>` expression with `Path<'_, T>`, rather than inserting `'_, ` + // at a point (which makes for an ugly/confusing label) + if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) { + // But our spans can get out of whack due to macros; if the place we think + // we want to insert `'_` isn't even within the path expression's span, we + // should bail out of making any suggestion rather than panicking on a + // subtract-with-overflow or string-slice-out-out-bounds (!) + // FIXME: can we do better? + if insertion_span.lo().0 < path_span.lo().0 { + return; + } + let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize; + if insertion_index > snippet.len() { + return; + } + let (before, after) = snippet.split_at(insertion_index); + (path_span, format!("{}{}{}", before, anon_lts, after)) + } else { + (insertion_span, anon_lts) + } + }; + db.span_suggestion( + replace_span, + &format!("indicate the anonymous lifetime{}", pluralize!(n)), + suggestion, + Applicability::MachineApplicable, + ); +} diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs new file mode 100644 index 00000000000..2db4d2a7f51 --- /dev/null +++ b/compiler/rustc_session/src/lint/builtin.rs @@ -0,0 +1,624 @@ +//! Some lints that are built in to the compiler. +//! +//! These are the built-in lints that are emitted direct in the main +//! compiler code, rather than using their own custom pass. Those +//! lints are all available in `rustc_lint::builtin`. + +use crate::lint::FutureIncompatibleInfo; +use crate::{declare_lint, declare_lint_pass}; +use rustc_span::edition::Edition; +use rustc_span::symbol::sym; + +declare_lint! { + pub ILL_FORMED_ATTRIBUTE_INPUT, + Deny, + "ill-formed attribute inputs that were previously accepted and used in practice", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #57571 <https://github.com/rust-lang/rust/issues/57571>", + edition: None, + }; + crate_level_only +} + +declare_lint! { + pub CONFLICTING_REPR_HINTS, + Deny, + "conflicts between `#[repr(..)]` hints that were previously accepted and used in practice", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #68585 <https://github.com/rust-lang/rust/issues/68585>", + edition: None, + }; +} + +declare_lint! { + pub META_VARIABLE_MISUSE, + Allow, + "possible meta-variable misuse at macro definition" +} + +declare_lint! { + pub INCOMPLETE_INCLUDE, + Deny, + "trailing content in included file" +} + +declare_lint! { + pub ARITHMETIC_OVERFLOW, + Deny, + "arithmetic operation overflows" +} + +declare_lint! { + pub UNCONDITIONAL_PANIC, + Deny, + "operation will cause a panic at runtime" +} + +declare_lint! { + pub CONST_ERR, + Deny, + "constant evaluation detected erroneous expression", + report_in_external_macro +} + +declare_lint! { + pub UNUSED_IMPORTS, + Warn, + "imports that are never used" +} + +declare_lint! { + pub UNUSED_EXTERN_CRATES, + Allow, + "extern crates that are never used" +} + +declare_lint! { + pub UNUSED_CRATE_DEPENDENCIES, + Allow, + "crate dependencies that are never used", + crate_level_only +} + +declare_lint! { + pub UNUSED_QUALIFICATIONS, + Allow, + "detects unnecessarily qualified names" +} + +declare_lint! { + pub UNKNOWN_LINTS, + Warn, + "unrecognized lint attribute" +} + +declare_lint! { + pub UNUSED_VARIABLES, + Warn, + "detect variables which are not used in any way" +} + +declare_lint! { + pub UNUSED_ASSIGNMENTS, + Warn, + "detect assignments that will never be read" +} + +declare_lint! { + pub DEAD_CODE, + Warn, + "detect unused, unexported items" +} + +declare_lint! { + pub UNUSED_ATTRIBUTES, + Warn, + "detects attributes that were not used by the compiler" +} + +declare_lint! { + pub UNREACHABLE_CODE, + Warn, + "detects unreachable code paths", + report_in_external_macro +} + +declare_lint! { + pub UNREACHABLE_PATTERNS, + Warn, + "detects unreachable patterns" +} + +declare_lint! { + pub OVERLAPPING_PATTERNS, + Warn, + "detects overlapping patterns" +} + +declare_lint! { + pub BINDINGS_WITH_VARIANT_NAME, + Warn, + "detects pattern bindings with the same name as one of the matched variants" +} + +declare_lint! { + pub UNUSED_MACROS, + Warn, + "detects macros that were not used" +} + +declare_lint! { + pub WARNINGS, + Warn, + "mass-change the level for lints which produce warnings" +} + +declare_lint! { + pub UNUSED_FEATURES, + Warn, + "unused features found in crate-level `#[feature]` directives" +} + +declare_lint! { + pub STABLE_FEATURES, + Warn, + "stable features found in `#[feature]` directive" +} + +declare_lint! { + pub UNKNOWN_CRATE_TYPES, + Deny, + "unknown crate type found in `#[crate_type]` directive", + crate_level_only +} + +declare_lint! { + pub TRIVIAL_CASTS, + Allow, + "detects trivial casts which could be removed" +} + +declare_lint! { + pub TRIVIAL_NUMERIC_CASTS, + Allow, + "detects trivial casts of numeric types which could be removed" +} + +declare_lint! { + pub PRIVATE_IN_PUBLIC, + Warn, + "detect private items in public interfaces not caught by the old implementation", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>", + edition: None, + }; +} + +declare_lint! { + pub EXPORTED_PRIVATE_DEPENDENCIES, + Warn, + "public interface leaks type from a private dependency" +} + +declare_lint! { + pub PUB_USE_OF_PRIVATE_EXTERN_CRATE, + Deny, + "detect public re-exports of private extern crates", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>", + edition: None, + }; +} + +declare_lint! { + pub INVALID_TYPE_PARAM_DEFAULT, + Deny, + "type parameter default erroneously allowed in invalid location", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #36887 <https://github.com/rust-lang/rust/issues/36887>", + edition: None, + }; +} + +declare_lint! { + pub RENAMED_AND_REMOVED_LINTS, + Warn, + "lints that have been renamed or removed" +} + +declare_lint! { + pub UNALIGNED_REFERENCES, + Allow, + "detects unaligned references to fields of packed structs", +} + +declare_lint! { + pub SAFE_PACKED_BORROWS, + Warn, + "safe borrows of fields of packed structs were erroneously allowed", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #46043 <https://github.com/rust-lang/rust/issues/46043>", + edition: None, + }; +} + +declare_lint! { + pub PATTERNS_IN_FNS_WITHOUT_BODY, + Deny, + "patterns in functions without body were erroneously allowed", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #35203 <https://github.com/rust-lang/rust/issues/35203>", + edition: None, + }; +} + +declare_lint! { + pub LATE_BOUND_LIFETIME_ARGUMENTS, + Warn, + "detects generic lifetime arguments in path segments with late bound lifetime parameters", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #42868 <https://github.com/rust-lang/rust/issues/42868>", + edition: None, + }; +} + +declare_lint! { + pub ORDER_DEPENDENT_TRAIT_OBJECTS, + Deny, + "trait-object types were treated as different depending on marker-trait order", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #56484 <https://github.com/rust-lang/rust/issues/56484>", + edition: None, + }; +} + +declare_lint! { + pub COHERENCE_LEAK_CHECK, + Warn, + "distinct impls distinguished only by the leak-check code", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #56105 <https://github.com/rust-lang/rust/issues/56105>", + edition: None, + }; +} + +declare_lint! { + pub DEPRECATED, + Warn, + "detects use of deprecated items", + report_in_external_macro +} + +declare_lint! { + pub UNUSED_UNSAFE, + Warn, + "unnecessary use of an `unsafe` block" +} + +declare_lint! { + pub UNUSED_MUT, + Warn, + "detect mut variables which don't need to be mutable" +} + +declare_lint! { + pub UNCONDITIONAL_RECURSION, + Warn, + "functions that cannot return without calling themselves" +} + +declare_lint! { + pub SINGLE_USE_LIFETIMES, + Allow, + "detects lifetime parameters that are only used once" +} + +declare_lint! { + pub UNUSED_LIFETIMES, + Allow, + "detects lifetime parameters that are never used" +} + +declare_lint! { + pub TYVAR_BEHIND_RAW_POINTER, + Warn, + "raw pointer to an inference variable", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #46906 <https://github.com/rust-lang/rust/issues/46906>", + edition: Some(Edition::Edition2018), + }; +} + +declare_lint! { + pub ELIDED_LIFETIMES_IN_PATHS, + Allow, + "hidden lifetime parameters in types are deprecated", + crate_level_only +} + +declare_lint! { + pub BARE_TRAIT_OBJECTS, + Warn, + "suggest using `dyn Trait` for trait objects" +} + +declare_lint! { + pub ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, + Allow, + "fully qualified paths that start with a module name \ + instead of `crate`, `self`, or an extern crate name", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #53130 <https://github.com/rust-lang/rust/issues/53130>", + edition: Some(Edition::Edition2018), + }; +} + +declare_lint! { + pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + Warn, + "floating-point literals cannot be used in patterns", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #41620 <https://github.com/rust-lang/rust/issues/41620>", + edition: None, + }; +} + +declare_lint! { + pub UNSTABLE_NAME_COLLISIONS, + Warn, + "detects name collision with an existing but unstable method", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #48919 <https://github.com/rust-lang/rust/issues/48919>", + edition: None, + // Note: this item represents future incompatibility of all unstable functions in the + // standard library, and thus should never be removed or changed to an error. + }; +} + +declare_lint! { + pub IRREFUTABLE_LET_PATTERNS, + Warn, + "detects irrefutable patterns in if-let and while-let statements" +} + +declare_lint! { + pub UNUSED_LABELS, + Warn, + "detects labels that are never used" +} + +declare_lint! { + pub BROKEN_INTRA_DOC_LINKS, + Warn, + "failures in resolving intra-doc link targets" +} + +declare_lint! { + pub INVALID_CODEBLOCK_ATTRIBUTES, + Warn, + "codeblock attribute looks a lot like a known one" +} + +declare_lint! { + pub MISSING_CRATE_LEVEL_DOCS, + Allow, + "detects crates with no crate-level documentation" +} + +declare_lint! { + pub MISSING_DOC_CODE_EXAMPLES, + Allow, + "detects publicly-exported items without code samples in their documentation" +} + +declare_lint! { + pub PRIVATE_DOC_TESTS, + Allow, + "detects code samples in docs of private items not documented by rustdoc" +} + +declare_lint! { + pub WHERE_CLAUSES_OBJECT_SAFETY, + Warn, + "checks the object safety of where clauses", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #51443 <https://github.com/rust-lang/rust/issues/51443>", + edition: None, + }; +} + +declare_lint! { + pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, + Warn, + "detects proc macro derives using inaccessible names from parent modules", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #50504 <https://github.com/rust-lang/rust/issues/50504>", + edition: None, + }; +} + +declare_lint! { + pub MACRO_USE_EXTERN_CRATE, + Allow, + "the `#[macro_use]` attribute is now deprecated in favor of using macros \ + via the module system" +} + +declare_lint! { + pub MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + Deny, + "macro-expanded `macro_export` macros from the current crate \ + cannot be referred to by absolute paths", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #52234 <https://github.com/rust-lang/rust/issues/52234>", + edition: None, + }; + crate_level_only +} + +declare_lint! { + pub EXPLICIT_OUTLIVES_REQUIREMENTS, + Allow, + "outlives requirements can be inferred" +} + +declare_lint! { + pub INDIRECT_STRUCTURAL_MATCH, + // defaulting to allow until rust-lang/rust#62614 is fixed. + Allow, + "pattern with const indirectly referencing non-structural-match type", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #62411 <https://github.com/rust-lang/rust/issues/62411>", + edition: None, + }; +} + +declare_lint! { + pub DEPRECATED_IN_FUTURE, + Allow, + "detects use of items that will be deprecated in a future version", + report_in_external_macro +} + +declare_lint! { + pub AMBIGUOUS_ASSOCIATED_ITEMS, + Deny, + "ambiguous associated items", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #57644 <https://github.com/rust-lang/rust/issues/57644>", + edition: None, + }; +} + +declare_lint! { + pub MUTABLE_BORROW_RESERVATION_CONFLICT, + Warn, + "reservation of a two-phased borrow conflicts with other shared borrows", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>", + edition: None, + }; +} + +declare_lint! { + pub SOFT_UNSTABLE, + Deny, + "a feature gate that doesn't break dependent crates", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #64266 <https://github.com/rust-lang/rust/issues/64266>", + edition: None, + }; +} + +declare_lint! { + pub INLINE_NO_SANITIZE, + Warn, + "detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`", +} + +declare_lint! { + pub ASM_SUB_REGISTER, + Warn, + "using only a subset of a register for inline asm inputs", +} + +declare_lint! { + pub UNSAFE_OP_IN_UNSAFE_FN, + Allow, + "unsafe operations in unsafe functions without an explicit unsafe block are deprecated", + @feature_gate = sym::unsafe_block_in_unsafe_fn; +} + +declare_lint! { + pub CENUM_IMPL_DROP_CAST, + Warn, + "a C-like enum implementing Drop is cast", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #73333 <https://github.com/rust-lang/rust/issues/73333>", + edition: None, + }; +} + +declare_lint_pass! { + /// Does nothing as a lint pass, but registers some `Lint`s + /// that are used by other parts of the compiler. + HardwiredLints => [ + ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + ARITHMETIC_OVERFLOW, + UNCONDITIONAL_PANIC, + UNUSED_IMPORTS, + UNUSED_EXTERN_CRATES, + UNUSED_CRATE_DEPENDENCIES, + UNUSED_QUALIFICATIONS, + UNKNOWN_LINTS, + UNUSED_VARIABLES, + UNUSED_ASSIGNMENTS, + DEAD_CODE, + UNREACHABLE_CODE, + UNREACHABLE_PATTERNS, + OVERLAPPING_PATTERNS, + BINDINGS_WITH_VARIANT_NAME, + UNUSED_MACROS, + WARNINGS, + UNUSED_FEATURES, + STABLE_FEATURES, + UNKNOWN_CRATE_TYPES, + TRIVIAL_CASTS, + TRIVIAL_NUMERIC_CASTS, + PRIVATE_IN_PUBLIC, + EXPORTED_PRIVATE_DEPENDENCIES, + PUB_USE_OF_PRIVATE_EXTERN_CRATE, + INVALID_TYPE_PARAM_DEFAULT, + CONST_ERR, + RENAMED_AND_REMOVED_LINTS, + UNALIGNED_REFERENCES, + SAFE_PACKED_BORROWS, + PATTERNS_IN_FNS_WITHOUT_BODY, + LATE_BOUND_LIFETIME_ARGUMENTS, + ORDER_DEPENDENT_TRAIT_OBJECTS, + COHERENCE_LEAK_CHECK, + DEPRECATED, + UNUSED_UNSAFE, + UNUSED_MUT, + UNCONDITIONAL_RECURSION, + SINGLE_USE_LIFETIMES, + UNUSED_LIFETIMES, + UNUSED_LABELS, + TYVAR_BEHIND_RAW_POINTER, + ELIDED_LIFETIMES_IN_PATHS, + BARE_TRAIT_OBJECTS, + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, + UNSTABLE_NAME_COLLISIONS, + IRREFUTABLE_LET_PATTERNS, + BROKEN_INTRA_DOC_LINKS, + INVALID_CODEBLOCK_ATTRIBUTES, + MISSING_CRATE_LEVEL_DOCS, + MISSING_DOC_CODE_EXAMPLES, + PRIVATE_DOC_TESTS, + WHERE_CLAUSES_OBJECT_SAFETY, + PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, + MACRO_USE_EXTERN_CRATE, + MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + ILL_FORMED_ATTRIBUTE_INPUT, + CONFLICTING_REPR_HINTS, + META_VARIABLE_MISUSE, + DEPRECATED_IN_FUTURE, + AMBIGUOUS_ASSOCIATED_ITEMS, + MUTABLE_BORROW_RESERVATION_CONFLICT, + INDIRECT_STRUCTURAL_MATCH, + SOFT_UNSTABLE, + INLINE_NO_SANITIZE, + ASM_SUB_REGISTER, + UNSAFE_OP_IN_UNSAFE_FN, + INCOMPLETE_INCLUDE, + CENUM_IMPL_DROP_CAST, + ] +} + +declare_lint! { + pub UNUSED_DOC_COMMENTS, + Warn, + "detects doc comments that aren't used by rustdoc" +} + +declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs new file mode 100644 index 00000000000..d05f1a3f34b --- /dev/null +++ b/compiler/rustc_session/src/options.rs @@ -0,0 +1,1078 @@ +use crate::config::*; + +use crate::early_error; +use crate::lint; +use crate::search_paths::SearchPath; +use crate::utils::NativeLibKind; + +use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy}; +use rustc_target::spec::{RelocModel, RelroLevel, TargetTriple, TlsModel}; + +use rustc_feature::UnstableFeatures; +use rustc_span::edition::Edition; +use rustc_span::SourceFileHashAlgorithm; + +use std::collections::BTreeMap; + +use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; +use std::path::PathBuf; +use std::str; + +macro_rules! hash_option { + ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [UNTRACKED]) => {{}}; + ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [TRACKED]) => {{ + if $sub_hashes + .insert(stringify!($opt_name), $opt_expr as &dyn dep_tracking::DepTrackingHash) + .is_some() + { + panic!("duplicate key in CLI DepTrackingHash: {}", stringify!($opt_name)) + } + }}; +} + +macro_rules! top_level_options { + (pub struct Options { $( + $opt:ident : $t:ty [$dep_tracking_marker:ident $($warn_val:expr, $warn_text:expr)*], + )* } ) => ( + #[derive(Clone)] + pub struct Options { + $(pub $opt: $t),* + } + + impl Options { + pub fn dep_tracking_hash(&self) -> u64 { + let mut sub_hashes = BTreeMap::new(); + $({ + hash_option!($opt, + &self.$opt, + &mut sub_hashes, + [$dep_tracking_marker $($warn_val, + $warn_text, + self.error_format)*]); + })* + let mut hasher = DefaultHasher::new(); + dep_tracking::stable_hash(sub_hashes, + &mut hasher, + self.error_format); + hasher.finish() + } + } + ); +} + +// The top-level command-line options struct. +// +// For each option, one has to specify how it behaves with regard to the +// dependency tracking system of incremental compilation. This is done via the +// square-bracketed directive after the field type. The options are: +// +// [TRACKED] +// A change in the given field will cause the compiler to completely clear the +// incremental compilation cache before proceeding. +// +// [UNTRACKED] +// Incremental compilation is not influenced by this option. +// +// If you add a new option to this struct or one of the sub-structs like +// `CodegenOptions`, think about how it influences incremental compilation. If in +// doubt, specify [TRACKED], which is always "correct" but might lead to +// unnecessary re-compilation. +top_level_options!( + pub struct Options { + // The crate config requested for the session, which may be combined + // with additional crate configurations during the compile process. + crate_types: Vec<CrateType> [TRACKED], + optimize: OptLevel [TRACKED], + // Include the `debug_assertions` flag in dependency tracking, since it + // can influence whether overflow checks are done or not. + debug_assertions: bool [TRACKED], + debuginfo: DebugInfo [TRACKED], + lint_opts: Vec<(String, lint::Level)> [TRACKED], + lint_cap: Option<lint::Level> [TRACKED], + describe_lints: bool [UNTRACKED], + output_types: OutputTypes [TRACKED], + search_paths: Vec<SearchPath> [UNTRACKED], + libs: Vec<(String, Option<String>, NativeLibKind)> [TRACKED], + maybe_sysroot: Option<PathBuf> [UNTRACKED], + + target_triple: TargetTriple [TRACKED], + + test: bool [TRACKED], + error_format: ErrorOutputType [UNTRACKED], + + // If `Some`, enable incremental compilation, using the given + // directory to store intermediate results. + incremental: Option<PathBuf> [UNTRACKED], + + debugging_opts: DebuggingOptions [TRACKED], + prints: Vec<PrintRequest> [UNTRACKED], + // Determines which borrow checker(s) to run. This is the parsed, sanitized + // version of `debugging_opts.borrowck`, which is just a plain string. + borrowck_mode: BorrowckMode [UNTRACKED], + cg: CodegenOptions [TRACKED], + externs: Externs [UNTRACKED], + crate_name: Option<String> [TRACKED], + // An optional name to use as the crate for std during std injection, + // written `extern crate name as std`. Defaults to `std`. Used by + // out-of-tree drivers. + alt_std_name: Option<String> [TRACKED], + // Indicates how the compiler should treat unstable features. + unstable_features: UnstableFeatures [TRACKED], + + // Indicates whether this run of the compiler is actually rustdoc. This + // is currently just a hack and will be removed eventually, so please + // try to not rely on this too much. + actually_rustdoc: bool [TRACKED], + + // Specifications of codegen units / ThinLTO which are forced as a + // result of parsing command line options. These are not necessarily + // what rustc was invoked with, but massaged a bit to agree with + // commands like `--emit llvm-ir` which they're often incompatible with + // if we otherwise use the defaults of rustc. + cli_forced_codegen_units: Option<usize> [UNTRACKED], + cli_forced_thinlto_off: bool [UNTRACKED], + + // Remap source path prefixes in all output (messages, object files, debug, etc.). + remap_path_prefix: Vec<(PathBuf, PathBuf)> [UNTRACKED], + + edition: Edition [TRACKED], + + // `true` if we're emitting JSON blobs about each artifact produced + // by the compiler. + json_artifact_notifications: bool [TRACKED], + + pretty: Option<PpMode> [UNTRACKED], + } +); + +/// Defines all `CodegenOptions`/`DebuggingOptions` fields and parsers all at once. The goal of this +/// macro is to define an interface that can be programmatically used by the option parser +/// to initialize the struct without hardcoding field names all over the place. +/// +/// The goal is to invoke this macro once with the correct fields, and then this macro generates all +/// necessary code. The main gotcha of this macro is the `cgsetters` module which is a bunch of +/// generated code to parse an option into its respective field in the struct. There are a few +/// hand-written parsers for parsing specific types of values in this module. +macro_rules! options { + ($struct_name:ident, $setter_name:ident, $defaultfn:ident, + $buildfn:ident, $prefix:expr, $outputname:expr, + $stat:ident, $mod_desc:ident, $mod_set:ident, + $($opt:ident : $t:ty = ( + $init:expr, + $parse:ident, + [$dep_tracking_marker:ident $(($dep_warn_val:expr, $dep_warn_text:expr))*], + $desc:expr) + ),* ,) => +( + #[derive(Clone)] + pub struct $struct_name { $(pub $opt: $t),* } + + pub fn $defaultfn() -> $struct_name { + $struct_name { $($opt: $init),* } + } + + pub fn $buildfn(matches: &getopts::Matches, error_format: ErrorOutputType) -> $struct_name + { + let mut op = $defaultfn(); + for option in matches.opt_strs($prefix) { + let mut iter = option.splitn(2, '='); + let key = iter.next().unwrap(); + let value = iter.next(); + let option_to_lookup = key.replace("-", "_"); + let mut found = false; + for &(candidate, setter, type_desc, _) in $stat { + if option_to_lookup != candidate { continue } + if !setter(&mut op, value) { + match value { + None => { + early_error(error_format, &format!("{0} option `{1}` requires \ + {2} ({3} {1}=<value>)", + $outputname, key, + type_desc, $prefix)) + } + Some(value) => { + early_error(error_format, &format!("incorrect value `{}` for {} \ + option `{}` - {} was expected", + value, $outputname, + key, type_desc)) + } + } + } + found = true; + break; + } + if !found { + early_error(error_format, &format!("unknown {} option: `{}`", + $outputname, key)); + } + } + return op; + } + + impl dep_tracking::DepTrackingHash for $struct_name { + fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType) { + let mut sub_hashes = BTreeMap::new(); + $({ + hash_option!($opt, + &self.$opt, + &mut sub_hashes, + [$dep_tracking_marker $($dep_warn_val, + $dep_warn_text, + error_format)*]); + })* + dep_tracking::stable_hash(sub_hashes, hasher, error_format); + } + } + + pub type $setter_name = fn(&mut $struct_name, v: Option<&str>) -> bool; + pub const $stat: &[(&str, $setter_name, &str, &str)] = + &[ $( (stringify!($opt), $mod_set::$opt, $mod_desc::$parse, $desc) ),* ]; + + #[allow(non_upper_case_globals, dead_code)] + mod $mod_desc { + pub const parse_no_flag: &str = "no value"; + pub const parse_bool: &str = "one of: `y`, `yes`, `on`, `n`, `no`, or `off`"; + pub const parse_opt_bool: &str = parse_bool; + pub const parse_string: &str = "a string"; + pub const parse_opt_string: &str = parse_string; + pub const parse_string_push: &str = parse_string; + pub const parse_opt_pathbuf: &str = "a path"; + pub const parse_pathbuf_push: &str = parse_opt_pathbuf; + pub const parse_list: &str = "a space-separated list of strings"; + pub const parse_opt_list: &str = parse_list; + pub const parse_opt_comma_list: &str = "a comma-separated list of strings"; + pub const parse_uint: &str = "a number"; + pub const parse_opt_uint: &str = parse_uint; + pub const parse_threads: &str = parse_uint; + pub const parse_passes: &str = "a space-separated list of passes, or `all`"; + pub const parse_panic_strategy: &str = "either `unwind` or `abort`"; + pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; + pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `leak`, `memory` or `thread`"; + pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; + pub const parse_cfguard: &str = + "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; + pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; + pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of(); + pub const parse_optimization_fuel: &str = "crate=integer"; + pub const parse_unpretty: &str = "`string` or `string=string`"; + pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0"; + pub const parse_lto: &str = + "either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted"; + pub const parse_linker_plugin_lto: &str = + "either a boolean (`yes`, `no`, `on`, `off`, etc), or the path to the linker plugin"; + pub const parse_switch_with_opt_path: &str = + "an optional path to the profiling data output directory"; + pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`"; + pub const parse_symbol_mangling_version: &str = "either `legacy` or `v0` (RFC 2603)"; + pub const parse_src_file_hash: &str = "either `md5` or `sha1`"; + pub const parse_relocation_model: &str = + "one of supported relocation models (`rustc --print relocation-models`)"; + pub const parse_code_model: &str = + "one of supported code models (`rustc --print code-models`)"; + pub const parse_tls_model: &str = + "one of supported TLS models (`rustc --print tls-models`)"; + pub const parse_target_feature: &str = parse_string; + } + + #[allow(dead_code)] + mod $mod_set { + use super::*; + use std::str::FromStr; + + // Sometimes different options need to build a common structure. + // That structure can kept in one of the options' fields, the others become dummy. + macro_rules! redirect_field { + ($cg:ident.link_arg) => { $cg.link_args }; + ($cg:ident.pre_link_arg) => { $cg.pre_link_args }; + ($cg:ident.$field:ident) => { $cg.$field }; + } + + $( + pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool { + $parse(&mut redirect_field!(cg.$opt), v) + } + )* + + /// This is for boolean options that don't take a value and start with + /// `no-`. This style of option is deprecated. + fn parse_no_flag(slot: &mut bool, v: Option<&str>) -> bool { + match v { + None => { *slot = true; true } + Some(_) => false, + } + } + + /// Use this for any boolean option that has a static default. + fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool { + match v { + Some("y") | Some("yes") | Some("on") | None => { *slot = true; true } + Some("n") | Some("no") | Some("off") => { *slot = false; true } + _ => false, + } + } + + /// Use this for any boolean option that lacks a static default. (The + /// actions taken when such an option is not specified will depend on + /// other factors, such as other options, or target options.) + fn parse_opt_bool(slot: &mut Option<bool>, v: Option<&str>) -> bool { + match v { + Some("y") | Some("yes") | Some("on") | None => { *slot = Some(true); true } + Some("n") | Some("no") | Some("off") => { *slot = Some(false); true } + _ => false, + } + } + + /// Use this for any string option that has a static default. + fn parse_string(slot: &mut String, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = s.to_string(); true }, + None => false, + } + } + + /// Use this for any string option that lacks a static default. + fn parse_opt_string(slot: &mut Option<String>, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = Some(s.to_string()); true }, + None => false, + } + } + + fn parse_opt_pathbuf(slot: &mut Option<PathBuf>, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = Some(PathBuf::from(s)); true }, + None => false, + } + } + + fn parse_string_push(slot: &mut Vec<String>, v: Option<&str>) -> bool { + match v { + Some(s) => { slot.push(s.to_string()); true }, + None => false, + } + } + + fn parse_pathbuf_push(slot: &mut Vec<PathBuf>, v: Option<&str>) -> bool { + match v { + Some(s) => { slot.push(PathBuf::from(s)); true }, + None => false, + } + } + + fn parse_list(slot: &mut Vec<String>, v: Option<&str>) + -> bool { + match v { + Some(s) => { + slot.extend(s.split_whitespace().map(|s| s.to_string())); + true + }, + None => false, + } + } + + fn parse_opt_list(slot: &mut Option<Vec<String>>, v: Option<&str>) + -> bool { + match v { + Some(s) => { + let v = s.split_whitespace().map(|s| s.to_string()).collect(); + *slot = Some(v); + true + }, + None => false, + } + } + + fn parse_opt_comma_list(slot: &mut Option<Vec<String>>, v: Option<&str>) + -> bool { + match v { + Some(s) => { + let v = s.split(',').map(|s| s.to_string()).collect(); + *slot = Some(v); + true + }, + None => false, + } + } + + fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool { + match v.and_then(|s| s.parse().ok()) { + Some(0) => { *slot = ::num_cpus::get(); true }, + Some(i) => { *slot = i; true }, + None => false + } + } + + /// Use this for any uint option that has a static default. + fn parse_uint(slot: &mut usize, v: Option<&str>) -> bool { + match v.and_then(|s| s.parse().ok()) { + Some(i) => { *slot = i; true }, + None => false + } + } + + /// Use this for any uint option that lacks a static default. + fn parse_opt_uint(slot: &mut Option<usize>, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = s.parse().ok(); slot.is_some() } + None => false + } + } + + fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool { + match v { + Some("all") => { + *slot = Passes::All; + true + } + v => { + let mut passes = vec![]; + if parse_list(&mut passes, v) { + *slot = Passes::Some(passes); + true + } else { + false + } + } + } + } + + fn parse_panic_strategy(slot: &mut Option<PanicStrategy>, v: Option<&str>) -> bool { + match v { + Some("unwind") => *slot = Some(PanicStrategy::Unwind), + Some("abort") => *slot = Some(PanicStrategy::Abort), + _ => return false + } + true + } + + fn parse_relro_level(slot: &mut Option<RelroLevel>, v: Option<&str>) -> bool { + match v { + Some(s) => { + match s.parse::<RelroLevel>() { + Ok(level) => *slot = Some(level), + _ => return false + } + }, + _ => return false + } + true + } + + fn parse_sanitizers(slot: &mut SanitizerSet, v: Option<&str>) -> bool { + if let Some(v) = v { + for s in v.split(',') { + *slot |= match s { + "address" => SanitizerSet::ADDRESS, + "leak" => SanitizerSet::LEAK, + "memory" => SanitizerSet::MEMORY, + "thread" => SanitizerSet::THREAD, + _ => return false, + } + } + true + } else { + false + } + } + + fn parse_sanitizer_memory_track_origins(slot: &mut usize, v: Option<&str>) -> bool { + match v { + Some("2") | None => { *slot = 2; true } + Some("1") => { *slot = 1; true } + Some("0") => { *slot = 0; true } + Some(_) => false, + } + } + + fn parse_strip(slot: &mut Strip, v: Option<&str>) -> bool { + match v { + Some("none") => *slot = Strip::None, + Some("debuginfo") => *slot = Strip::Debuginfo, + Some("symbols") => *slot = Strip::Symbols, + _ => return false, + } + true + } + + fn parse_cfguard(slot: &mut CFGuard, v: Option<&str>) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + CFGuard::Checks + } else { + CFGuard::Disabled + }; + return true + } + } + + *slot = match v { + None => CFGuard::Checks, + Some("checks") => CFGuard::Checks, + Some("nochecks") => CFGuard::NoChecks, + Some(_) => return false, + }; + true + } + + fn parse_linker_flavor(slote: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool { + match v.and_then(LinkerFlavor::from_str) { + Some(lf) => *slote = Some(lf), + _ => return false, + } + true + } + + fn parse_optimization_fuel(slot: &mut Option<(String, u64)>, v: Option<&str>) -> bool { + match v { + None => false, + Some(s) => { + let parts = s.split('=').collect::<Vec<_>>(); + if parts.len() != 2 { return false; } + let crate_name = parts[0].to_string(); + let fuel = parts[1].parse::<u64>(); + if fuel.is_err() { return false; } + *slot = Some((crate_name, fuel.unwrap())); + true + } + } + } + + fn parse_unpretty(slot: &mut Option<String>, v: Option<&str>) -> bool { + match v { + None => false, + Some(s) if s.split('=').count() <= 2 => { + *slot = Some(s.to_string()); + true + } + _ => false, + } + } + + fn parse_treat_err_as_bug(slot: &mut Option<usize>, v: Option<&str>) -> bool { + match v { + Some(s) => { *slot = s.parse().ok().filter(|&x| x != 0); slot.unwrap_or(0) != 0 } + None => { *slot = Some(1); true } + } + } + + fn parse_lto(slot: &mut LtoCli, v: Option<&str>) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + LtoCli::Yes + } else { + LtoCli::No + }; + return true + } + } + + *slot = match v { + None => LtoCli::NoParam, + Some("thin") => LtoCli::Thin, + Some("fat") => LtoCli::Fat, + Some(_) => return false, + }; + true + } + + fn parse_linker_plugin_lto(slot: &mut LinkerPluginLto, v: Option<&str>) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + LinkerPluginLto::LinkerPluginAuto + } else { + LinkerPluginLto::Disabled + }; + return true + } + } + + *slot = match v { + None => LinkerPluginLto::LinkerPluginAuto, + Some(path) => LinkerPluginLto::LinkerPlugin(PathBuf::from(path)), + }; + true + } + + fn parse_switch_with_opt_path(slot: &mut SwitchWithOptPath, v: Option<&str>) -> bool { + *slot = match v { + None => SwitchWithOptPath::Enabled(None), + Some(path) => SwitchWithOptPath::Enabled(Some(PathBuf::from(path))), + }; + true + } + + fn parse_merge_functions(slot: &mut Option<MergeFunctions>, v: Option<&str>) -> bool { + match v.and_then(|s| MergeFunctions::from_str(s).ok()) { + Some(mergefunc) => *slot = Some(mergefunc), + _ => return false, + } + true + } + + fn parse_relocation_model(slot: &mut Option<RelocModel>, v: Option<&str>) -> bool { + match v.and_then(|s| RelocModel::from_str(s).ok()) { + Some(relocation_model) => *slot = Some(relocation_model), + None if v == Some("default") => *slot = None, + _ => return false, + } + true + } + + fn parse_code_model(slot: &mut Option<CodeModel>, v: Option<&str>) -> bool { + match v.and_then(|s| CodeModel::from_str(s).ok()) { + Some(code_model) => *slot = Some(code_model), + _ => return false, + } + true + } + + fn parse_tls_model(slot: &mut Option<TlsModel>, v: Option<&str>) -> bool { + match v.and_then(|s| TlsModel::from_str(s).ok()) { + Some(tls_model) => *slot = Some(tls_model), + _ => return false, + } + true + } + + fn parse_symbol_mangling_version( + slot: &mut SymbolManglingVersion, + v: Option<&str>, + ) -> bool { + *slot = match v { + Some("legacy") => SymbolManglingVersion::Legacy, + Some("v0") => SymbolManglingVersion::V0, + _ => return false, + }; + true + } + + fn parse_src_file_hash(slot: &mut Option<SourceFileHashAlgorithm>, v: Option<&str>) -> bool { + match v.and_then(|s| SourceFileHashAlgorithm::from_str(s).ok()) { + Some(hash_kind) => *slot = Some(hash_kind), + _ => return false, + } + true + } + + fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool { + match v { + Some(s) => { + if !slot.is_empty() { + slot.push_str(","); + } + slot.push_str(s); + true + } + None => false, + } + } + } +) } + +options! {CodegenOptions, CodegenSetter, basic_codegen_options, + build_codegen_options, "C", "codegen", + CG_OPTIONS, cg_type_desc, cgsetters, + + // This list is in alphabetical order. + // + // If you add a new option, please update: + // - src/librustc_interface/tests.rs + // - src/doc/rustc/src/codegen-options/index.md + + ar: String = (String::new(), parse_string, [UNTRACKED], + "this option is deprecated and does nothing"), + code_model: Option<CodeModel> = (None, parse_code_model, [TRACKED], + "choose the code model to use (`rustc --print code-models` for details)"), + codegen_units: Option<usize> = (None, parse_opt_uint, [UNTRACKED], + "divide crate into N units to optimize in parallel"), + control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [TRACKED], + "use Windows Control Flow Guard (default: no)"), + debug_assertions: Option<bool> = (None, parse_opt_bool, [TRACKED], + "explicitly enable the `cfg(debug_assertions)` directive"), + debuginfo: usize = (0, parse_uint, [TRACKED], + "debug info emission level (0 = no debug info, 1 = line tables only, \ + 2 = full debug info with variable and type information; default: 0)"), + default_linker_libraries: bool = (false, parse_bool, [UNTRACKED], + "allow the linker to link its default libraries (default: no)"), + embed_bitcode: bool = (true, parse_bool, [TRACKED], + "emit bitcode in rlibs (default: yes)"), + extra_filename: String = (String::new(), parse_string, [UNTRACKED], + "extra data to put in each output filename"), + force_frame_pointers: Option<bool> = (None, parse_opt_bool, [TRACKED], + "force use of the frame pointers"), + force_unwind_tables: Option<bool> = (None, parse_opt_bool, [TRACKED], + "force use of unwind tables"), + incremental: Option<String> = (None, parse_opt_string, [UNTRACKED], + "enable incremental compilation"), + inline_threshold: Option<usize> = (None, parse_opt_uint, [TRACKED], + "set the threshold for inlining a function"), + link_arg: (/* redirected to link_args */) = ((), parse_string_push, [UNTRACKED], + "a single extra argument to append to the linker invocation (can be used several times)"), + link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED], + "extra arguments to append to the linker invocation (space separated)"), + link_dead_code: Option<bool> = (None, parse_opt_bool, [UNTRACKED], + "keep dead code at link time (useful for code coverage) (default: no)"), + linker: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED], + "system linker to link outputs with"), + linker_flavor: Option<LinkerFlavor> = (None, parse_linker_flavor, [UNTRACKED], + "linker flavor"), + linker_plugin_lto: LinkerPluginLto = (LinkerPluginLto::Disabled, + parse_linker_plugin_lto, [TRACKED], + "generate build artifacts that are compatible with linker-based LTO"), + llvm_args: Vec<String> = (Vec::new(), parse_list, [TRACKED], + "a list of arguments to pass to LLVM (space separated)"), + lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED], + "perform LLVM link-time optimizations"), + metadata: Vec<String> = (Vec::new(), parse_list, [TRACKED], + "metadata to mangle symbol names with"), + no_prepopulate_passes: bool = (false, parse_no_flag, [TRACKED], + "give an empty list of passes to the pass manager"), + no_redzone: Option<bool> = (None, parse_opt_bool, [TRACKED], + "disable the use of the redzone"), + no_stack_check: bool = (false, parse_no_flag, [UNTRACKED], + "this option is deprecated and does nothing"), + no_vectorize_loops: bool = (false, parse_no_flag, [TRACKED], + "disable loop vectorization optimization passes"), + no_vectorize_slp: bool = (false, parse_no_flag, [TRACKED], + "disable LLVM's SLP vectorization pass"), + opt_level: String = ("0".to_string(), parse_string, [TRACKED], + "optimization level (0-3, s, or z; default: 0)"), + overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED], + "use overflow checks for integer arithmetic"), + panic: Option<PanicStrategy> = (None, parse_panic_strategy, [TRACKED], + "panic strategy to compile crate with"), + passes: Vec<String> = (Vec::new(), parse_list, [TRACKED], + "a list of extra LLVM passes to run (space separated)"), + prefer_dynamic: bool = (false, parse_bool, [TRACKED], + "prefer dynamic linking to static linking (default: no)"), + profile_generate: SwitchWithOptPath = (SwitchWithOptPath::Disabled, + parse_switch_with_opt_path, [TRACKED], + "compile the program with profiling instrumentation"), + profile_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], + "use the given `.profdata` file for profile-guided optimization"), + relocation_model: Option<RelocModel> = (None, parse_relocation_model, [TRACKED], + "control generation of position-independent code (PIC) \ + (`rustc --print relocation-models` for details)"), + remark: Passes = (Passes::Some(Vec::new()), parse_passes, [UNTRACKED], + "print remarks for these optimization passes (space separated, or \"all\")"), + rpath: bool = (false, parse_bool, [UNTRACKED], + "set rpath values in libs/exes (default: no)"), + save_temps: bool = (false, parse_bool, [UNTRACKED], + "save all temporary output files during compilation (default: no)"), + soft_float: bool = (false, parse_bool, [TRACKED], + "use soft float ABI (*eabihf targets only) (default: no)"), + target_cpu: Option<String> = (None, parse_opt_string, [TRACKED], + "select target processor (`rustc --print target-cpus` for details)"), + target_feature: String = (String::new(), parse_target_feature, [TRACKED], + "target specific attributes. (`rustc --print target-features` for details). \ + This feature is unsafe."), + + // This list is in alphabetical order. + // + // If you add a new option, please update: + // - src/librustc_interface/tests.rs + // - src/doc/rustc/src/codegen-options/index.md +} + +options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, + build_debugging_options, "Z", "debugging", + DB_OPTIONS, db_type_desc, dbsetters, + + // This list is in alphabetical order. + // + // If you add a new option, please update: + // - src/librustc_interface/tests.rs + + allow_features: Option<Vec<String>> = (None, parse_opt_comma_list, [TRACKED], + "only allow the listed language features to be enabled in code (space separated)"), + always_encode_mir: bool = (false, parse_bool, [TRACKED], + "encode MIR of all functions into the crate metadata (default: no)"), + asm_comments: bool = (false, parse_bool, [TRACKED], + "generate comments into the assembly (may change behavior) (default: no)"), + ast_json: bool = (false, parse_bool, [UNTRACKED], + "print the AST as JSON and halt (default: no)"), + ast_json_noexpand: bool = (false, parse_bool, [UNTRACKED], + "print the pre-expansion AST as JSON and halt (default: no)"), + binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], + "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ + (default: no)"), + borrowck: String = ("migrate".to_string(), parse_string, [UNTRACKED], + "select which borrowck is used (`mir` or `migrate`) (default: `migrate`)"), + borrowck_stats: bool = (false, parse_bool, [UNTRACKED], + "gather borrowck statistics (default: no)"), + cgu_partitioning_strategy: Option<String> = (None, parse_opt_string, [TRACKED], + "the codegen unit partitioning strategy to use"), + chalk: bool = (false, parse_bool, [TRACKED], + "enable the experimental Chalk-based trait solving engine"), + codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED], + "the backend to use"), + crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED], + "inject the given attribute in the crate"), + debug_macros: bool = (false, parse_bool, [TRACKED], + "emit line numbers debug info inside macros (default: no)"), + deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED], + "deduplicate identical diagnostics (default: yes)"), + dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED], + "in dep-info output, omit targets for tracking dependencies of the dep-info files \ + themselves (default: no)"), + dep_tasks: bool = (false, parse_bool, [UNTRACKED], + "print tasks that execute and the color their dep node gets (requires debug build) \ + (default: no)"), + dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], + "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \ + (default: no)"), + dual_proc_macros: bool = (false, parse_bool, [TRACKED], + "load proc macros for both target and host, but only link to the target (default: no)"), + dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], + "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) \ + (default: no)"), + dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED], + "dump MIR state to file. + `val` is used to select which passes and functions to dump. For example: + `all` matches all passes and functions, + `foo` matches all passes for functions whose name contains 'foo', + `foo & ConstProp` only the 'ConstProp' pass for function names containing 'foo', + `foo | bar` all passes for function names containing 'foo' or 'bar'."), + dump_mir_dataflow: bool = (false, parse_bool, [UNTRACKED], + "in addition to `.mir` files, create graphviz `.dot` files with dataflow results \ + (default: no)"), + dump_mir_dir: String = ("mir_dump".to_string(), parse_string, [UNTRACKED], + "the directory the MIR is dumped into (default: `mir_dump`)"), + dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED], + "exclude the pass number when dumping MIR (used in tests) (default: no)"), + dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], + "in addition to `.mir` files, create graphviz `.dot` files (default: no)"), + emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], + "emit a section containing stack size metadata (default: no)"), + fewer_names: bool = (false, parse_bool, [TRACKED], + "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ + (default: no)"), + force_overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED], + "force overflow checks on or off"), + force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], + "force all crates to be `rustc_private` unstable (default: no)"), + fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], + "set the optimization fuel quota for a crate"), + hir_stats: bool = (false, parse_bool, [UNTRACKED], + "print some statistics about AST and HIR (default: no)"), + human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], + "generate human-readable, predictable names for codegen units (default: no)"), + identify_regions: bool = (false, parse_bool, [UNTRACKED], + "display unnamed regions as `'<id>`, using a non-ident unique id (default: no)"), + incremental_ignore_spans: bool = (false, parse_bool, [UNTRACKED], + "ignore spans during ICH computation -- used for testing (default: no)"), + incremental_info: bool = (false, parse_bool, [UNTRACKED], + "print high-level information about incremental reuse (or the lack thereof) \ + (default: no)"), + incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], + "verify incr. comp. hashes of green query instances (default: no)"), + inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED], + "control whether `#[inline]` functions are in all CGUs"), + input_stats: bool = (false, parse_bool, [UNTRACKED], + "gather statistics about the input (default: no)"), + insert_sideeffect: bool = (false, parse_bool, [TRACKED], + "fix undefined behavior when a thread doesn't eventually make progress \ + (such as entering an empty infinite loop) by inserting llvm.sideeffect \ + (default: no)"), + instrument_coverage: bool = (false, parse_bool, [TRACKED], + "instrument the generated code to support LLVM source-based code coverage \ + reports (note, the compiler build config must include `profiler = true`, \ + and is mutually exclusive with `-C profile-generate`/`-C profile-use`); \ + implies `-C link-dead-code` (unless explicitly disabled)` and \ + `-Z symbol-mangling-version=v0`; and disables/overrides some optimization \ + options (default: no)"), + instrument_mcount: bool = (false, parse_bool, [TRACKED], + "insert function instrument code for mcount-based tracing (default: no)"), + keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], + "keep hygiene data after analysis (default: no)"), + link_native_libraries: bool = (true, parse_bool, [UNTRACKED], + "link native libraries in the linker invocation (default: yes)"), + link_self_contained: Option<bool> = (None, parse_opt_bool, [TRACKED], + "control whether to link Rust provided C objects/libraries or rely + on C toolchain installed in the system"), + link_only: bool = (false, parse_bool, [TRACKED], + "link the `.rlink` file generated by `-Z no-link` (default: no)"), + llvm_time_trace: bool = (false, parse_bool, [UNTRACKED], + "generate JSON tracing data file from LLVM data (default: no)"), + ls: bool = (false, parse_bool, [UNTRACKED], + "list the symbols defined by a library crate (default: no)"), + macro_backtrace: bool = (false, parse_bool, [UNTRACKED], + "show macro backtraces (default: no)"), + merge_functions: Option<MergeFunctions> = (None, parse_merge_functions, [TRACKED], + "control the operation of the MergeFunctions LLVM pass, taking \ + the same values as the target option of the same name"), + meta_stats: bool = (false, parse_bool, [UNTRACKED], + "gather metadata statistics (default: no)"), + mir_emit_retag: bool = (false, parse_bool, [TRACKED], + "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ + (default: no)"), + mir_opt_level: usize = (1, parse_uint, [TRACKED], + "MIR optimization level (0-3; default: 1)"), + mutable_noalias: bool = (false, parse_bool, [TRACKED], + "emit noalias metadata for mutable references (default: no)"), + new_llvm_pass_manager: bool = (false, parse_bool, [TRACKED], + "use new LLVM pass manager (default: no)"), + nll_facts: bool = (false, parse_bool, [UNTRACKED], + "dump facts from NLL analysis into side files (default: no)"), + no_analysis: bool = (false, parse_no_flag, [UNTRACKED], + "parse and expand the source, but run no analysis"), + no_codegen: bool = (false, parse_no_flag, [TRACKED], + "run all passes except codegen; no output"), + no_generate_arange_section: bool = (false, parse_no_flag, [TRACKED], + "omit DWARF address ranges that give faster lookups"), + no_interleave_lints: bool = (false, parse_no_flag, [UNTRACKED], + "execute lints separately; allows benchmarking individual lints"), + no_leak_check: bool = (false, parse_no_flag, [UNTRACKED], + "disable the 'leak check' for subtyping; unsound, but useful for tests"), + no_link: bool = (false, parse_no_flag, [TRACKED], + "compile without linking"), + no_parallel_llvm: bool = (false, parse_no_flag, [UNTRACKED], + "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"), + no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED], + "prevent automatic injection of the profiler_builtins crate"), + osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], + "pass `-install_name @rpath/...` to the macOS linker (default: no)"), + panic_abort_tests: bool = (false, parse_bool, [TRACKED], + "support compiling tests with panic=abort (default: no)"), + parse_only: bool = (false, parse_bool, [UNTRACKED], + "parse only; do not compile, assemble, or link (default: no)"), + perf_stats: bool = (false, parse_bool, [UNTRACKED], + "print some performance-related statistics (default: no)"), + plt: Option<bool> = (None, parse_opt_bool, [TRACKED], + "whether to use the PLT when calling into shared libraries; + only has effect for PIC code on systems with ELF binaries + (default: PLT is disabled if full relro is enabled)"), + polonius: bool = (false, parse_bool, [UNTRACKED], + "enable polonius-based borrow-checker (default: no)"), + polymorphize: bool = (false, parse_bool, [TRACKED], + "perform polymorphization analysis"), + pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], + "a single extra argument to prepend the linker invocation (can be used several times)"), + pre_link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED], + "extra arguments to prepend to the linker invocation (space separated)"), + print_fuel: Option<String> = (None, parse_opt_string, [TRACKED], + "make rustc print the total optimization fuel used by a crate"), + print_link_args: bool = (false, parse_bool, [UNTRACKED], + "print the arguments passed to the linker (default: no)"), + print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], + "print the LLVM optimization passes being run (default: no)"), + print_mono_items: Option<String> = (None, parse_opt_string, [UNTRACKED], + "print the result of the monomorphization collection pass"), + print_type_sizes: bool = (false, parse_bool, [UNTRACKED], + "print layout information for each type encountered (default: no)"), + profile: bool = (false, parse_bool, [TRACKED], + "insert profiling code (default: no)"), + profile_emit: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], + "file path to emit profiling data at runtime when using 'profile' \ + (default based on relative source path)"), + query_dep_graph: bool = (false, parse_bool, [UNTRACKED], + "enable queries of the dependency graph for regression testing (default: no)"), + query_stats: bool = (false, parse_bool, [UNTRACKED], + "print some statistics about the query system (default: no)"), + relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED], + "choose which RELRO level to use"), + report_delayed_bugs: bool = (false, parse_bool, [TRACKED], + "immediately print bugs registered with `delay_span_bug` (default: no)"), + // The default historical behavior was to always run dsymutil, so we're + // preserving that temporarily, but we're likely to switch the default + // soon. + run_dsymutil: bool = (true, parse_bool, [TRACKED], + "if on Mac, run `dsymutil` and delete intermediate object files (default: yes)"), + sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], + "use a sanitizer"), + sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], + "enable origins tracking in MemorySanitizer"), + sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], + "enable recovery for selected sanitizers"), + saturating_float_casts: Option<bool> = (None, parse_opt_bool, [TRACKED], + "make float->int casts UB-free: numbers outside the integer type's range are clipped to \ + the max/min integer respectively, and NaN is mapped to 0 (default: yes)"), + save_analysis: bool = (false, parse_bool, [UNTRACKED], + "write syntax and type analysis (in JSON format) information, in \ + addition to normal output (default: no)"), + self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled, + parse_switch_with_opt_path, [UNTRACKED], + "run the self profiler and output the raw event data"), + // keep this in sync with the event filter names in librustc_data_structures/profiling.rs + self_profile_events: Option<Vec<String>> = (None, parse_opt_comma_list, [UNTRACKED], + "specify the events recorded by the self profiler; + for example: `-Z self-profile-events=default,query-keys` + all options: none, all, default, generic-activity, query-provider, query-cache-hit + query-blocked, incr-cache-load, query-keys, function-args, args, llvm"), + share_generics: Option<bool> = (None, parse_opt_bool, [TRACKED], + "make the current crate share its generic instantiations"), + show_span: Option<String> = (None, parse_opt_string, [TRACKED], + "show spans for compiler debugging (expr|pat|ty)"), + span_debug: bool = (false, parse_bool, [UNTRACKED], + "forward proc_macro::Span's `Debug` impl to `Span`"), + // o/w tests have closure@path + span_free_formats: bool = (false, parse_bool, [UNTRACKED], + "exclude spans when debug-printing compiler state (default: no)"), + src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED], + "hash algorithm of source files in debug info (`md5`, or `sha1`)"), + strip: Strip = (Strip::None, parse_strip, [UNTRACKED], + "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), + symbol_mangling_version: SymbolManglingVersion = (SymbolManglingVersion::Legacy, + parse_symbol_mangling_version, [TRACKED], + "which mangling version to use for symbol names"), + teach: bool = (false, parse_bool, [TRACKED], + "show extended diagnostic help (default: no)"), + terminal_width: Option<usize> = (None, parse_opt_uint, [UNTRACKED], + "set the current terminal width"), + thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED], + "enable ThinLTO when possible"), + // We default to 1 here since we want to behave like + // a sequential compiler for now. This'll likely be adjusted + // in the future. Note that -Zthreads=0 is the way to get + // the num_cpus behavior. + threads: usize = (1, parse_threads, [UNTRACKED], + "use a thread pool with N threads"), + time: bool = (false, parse_bool, [UNTRACKED], + "measure time of rustc processes (default: no)"), + time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], + "measure time of each LLVM pass (default: no)"), + time_passes: bool = (false, parse_bool, [UNTRACKED], + "measure time of each rustc pass (default: no)"), + tls_model: Option<TlsModel> = (None, parse_tls_model, [TRACKED], + "choose the TLS model to use (`rustc --print tls-models` for details)"), + trace_macros: bool = (false, parse_bool, [UNTRACKED], + "for every macro invocation, print its name and arguments (default: no)"), + treat_err_as_bug: Option<usize> = (None, parse_treat_err_as_bug, [TRACKED], + "treat error number `val` that occurs as bug"), + ui_testing: bool = (false, parse_bool, [UNTRACKED], + "emit compiler diagnostics in a form suitable for UI testing (default: no)"), + unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], + "take the brakes off const evaluation. NOTE: this is unsound (default: no)"), + unpretty: Option<String> = (None, parse_unpretty, [UNTRACKED], + "present the input source, unstable (and less-pretty) variants; + valid types are any of the types for `--pretty`, as well as: + `expanded`, `expanded,identified`, + `expanded,hygiene` (with internal representations), + `everybody_loops` (all function bodies replaced with `loop {}`), + `hir` (the HIR), `hir,identified`, + `hir,typed` (HIR with types for each node), + `hir-tree` (dump the raw HIR), + `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR)"), + unstable_options: bool = (false, parse_bool, [UNTRACKED], + "adds unstable command line options to rustc interface (default: no)"), + use_ctors_section: Option<bool> = (None, parse_opt_bool, [TRACKED], + "use legacy .ctors section for initializers rather than .init_array"), + validate_mir: bool = (false, parse_bool, [UNTRACKED], + "validate MIR after each transformation"), + verbose: bool = (false, parse_bool, [UNTRACKED], + "in general, enable more debug printouts (default: no)"), + verify_llvm_ir: bool = (false, parse_bool, [TRACKED], + "verify LLVM IR (default: no)"), + + // This list is in alphabetical order. + // + // If you add a new option, please update: + // - src/librustc_interface/tests.rs +} diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs new file mode 100644 index 00000000000..bf9c96c6c94 --- /dev/null +++ b/compiler/rustc_session/src/output.rs @@ -0,0 +1,217 @@ +//! Related to out filenames of compilation (e.g. save analysis, binaries). +use crate::config::{CrateType, Input, OutputFilenames, OutputType}; +use crate::Session; +use rustc_ast as ast; +use rustc_span::symbol::sym; +use rustc_span::Span; +use std::path::{Path, PathBuf}; + +pub fn out_filename( + sess: &Session, + crate_type: CrateType, + outputs: &OutputFilenames, + crate_name: &str, +) -> PathBuf { + let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); + let out_filename = outputs + .outputs + .get(&OutputType::Exe) + .and_then(|s| s.to_owned()) + .or_else(|| outputs.single_output_file.clone()) + .unwrap_or(default_filename); + + check_file_is_writeable(&out_filename, sess); + + out_filename +} + +/// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers +/// check this already -- however, the Linux linker will happily overwrite a +/// read-only file. We should be consistent. +pub fn check_file_is_writeable(file: &Path, sess: &Session) { + if !is_writeable(file) { + sess.fatal(&format!( + "output file {} is not writeable -- check its \ + permissions", + file.display() + )); + } +} + +fn is_writeable(p: &Path) -> bool { + match p.metadata() { + Err(..) => true, + Ok(m) => !m.permissions().readonly(), + } +} + +pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input) -> String { + let validate = |s: String, span: Option<Span>| { + validate_crate_name(sess, &s, span); + s + }; + + // Look in attributes 100% of the time to make sure the attribute is marked + // as used. After doing this, however, we still prioritize a crate name from + // the command line over one found in the #[crate_name] attribute. If we + // find both we ensure that they're the same later on as well. + let attr_crate_name = + sess.find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s))); + + if let Some(ref s) = sess.opts.crate_name { + if let Some((attr, name)) = attr_crate_name { + if name.as_str() != *s { + let msg = format!( + "`--crate-name` and `#[crate_name]` are \ + required to match, but `{}` != `{}`", + s, name + ); + sess.span_err(attr.span, &msg); + } + } + return validate(s.clone(), None); + } + + if let Some((attr, s)) = attr_crate_name { + return validate(s.to_string(), Some(attr.span)); + } + if let Input::File(ref path) = *input { + if let Some(s) = path.file_stem().and_then(|s| s.to_str()) { + if s.starts_with('-') { + let msg = format!( + "crate names cannot start with a `-`, but \ + `{}` has a leading hyphen", + s + ); + sess.err(&msg); + } else { + return validate(s.replace("-", "_"), None); + } + } + } + + "rust_out".to_string() +} + +pub fn validate_crate_name(sess: &Session, s: &str, sp: Option<Span>) { + let mut err_count = 0; + { + let mut say = |s: &str| { + match sp { + Some(sp) => sess.span_err(sp, s), + None => sess.err(s), + } + err_count += 1; + }; + if s.is_empty() { + say("crate name must not be empty"); + } + for c in s.chars() { + if c.is_alphanumeric() { + continue; + } + if c == '_' { + continue; + } + say(&format!("invalid character `{}` in crate name: `{}`", c, s)); + } + } + + if err_count > 0 { + sess.abort_if_errors(); + } +} + +pub fn filename_for_metadata( + sess: &Session, + crate_name: &str, + outputs: &OutputFilenames, +) -> PathBuf { + let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); + + let out_filename = outputs + .single_output_file + .clone() + .unwrap_or_else(|| outputs.out_directory.join(&format!("lib{}.rmeta", libname))); + + check_file_is_writeable(&out_filename, sess); + + out_filename +} + +pub fn filename_for_input( + sess: &Session, + crate_type: CrateType, + crate_name: &str, + outputs: &OutputFilenames, +) -> PathBuf { + let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); + + match crate_type { + CrateType::Rlib => outputs.out_directory.join(&format!("lib{}.rlib", libname)), + CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib => { + let (prefix, suffix) = + (&sess.target.target.options.dll_prefix, &sess.target.target.options.dll_suffix); + outputs.out_directory.join(&format!("{}{}{}", prefix, libname, suffix)) + } + CrateType::Staticlib => { + let (prefix, suffix) = ( + &sess.target.target.options.staticlib_prefix, + &sess.target.target.options.staticlib_suffix, + ); + outputs.out_directory.join(&format!("{}{}{}", prefix, libname, suffix)) + } + CrateType::Executable => { + let suffix = &sess.target.target.options.exe_suffix; + let out_filename = outputs.path(OutputType::Exe); + if suffix.is_empty() { out_filename } else { out_filename.with_extension(&suffix[1..]) } + } + } +} + +/// Returns default crate type for target +/// +/// Default crate type is used when crate type isn't provided neither +/// through cmd line arguments nor through crate attributes +/// +/// It is CrateType::Executable for all platforms but iOS as there is no +/// way to run iOS binaries anyway without jailbreaking and +/// interaction with Rust code through static library is the only +/// option for now +pub fn default_output_for_target(sess: &Session) -> CrateType { + if !sess.target.target.options.executables { + CrateType::Staticlib + } else { + CrateType::Executable + } +} + +/// Checks if target supports crate_type as output +pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool { + match crate_type { + CrateType::Cdylib | CrateType::Dylib | CrateType::ProcMacro => { + if !sess.target.target.options.dynamic_linking { + return true; + } + if sess.crt_static(Some(crate_type)) + && !sess.target.target.options.crt_static_allows_dylibs + { + return true; + } + } + _ => {} + } + if sess.target.target.options.only_cdylib { + match crate_type { + CrateType::ProcMacro | CrateType::Dylib => return true, + _ => {} + } + } + if !sess.target.target.options.executables { + if crate_type == CrateType::Executable { + return true; + } + } + + false +} diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs new file mode 100644 index 00000000000..6f10d0c4b89 --- /dev/null +++ b/compiler/rustc_session/src/parse.rs @@ -0,0 +1,239 @@ +//! Contains `ParseSess` which holds state living beyond what one `Parser` might. +//! It also serves as an input to the parser itself. + +use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId}; +use rustc_ast::node_id::NodeId; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::sync::{Lock, Lrc, OnceCell}; +use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler}; +use rustc_errors::{error_code, Applicability, DiagnosticBuilder}; +use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; +use rustc_span::edition::Edition; +use rustc_span::hygiene::ExpnId; +use rustc_span::source_map::{FilePathMapping, SourceMap}; +use rustc_span::{MultiSpan, Span, Symbol}; + +use std::path::PathBuf; +use std::str; + +/// The set of keys (and, optionally, values) that define the compilation +/// environment of the crate, used to drive conditional compilation. +pub type CrateConfig = FxHashSet<(Symbol, Option<Symbol>)>; + +/// Collected spans during parsing for places where a certain feature was +/// used and should be feature gated accordingly in `check_crate`. +#[derive(Default)] +pub struct GatedSpans { + pub spans: Lock<FxHashMap<Symbol, Vec<Span>>>, +} + +impl GatedSpans { + /// Feature gate the given `span` under the given `feature` + /// which is same `Symbol` used in `active.rs`. + pub fn gate(&self, feature: Symbol, span: Span) { + self.spans.borrow_mut().entry(feature).or_default().push(span); + } + + /// Ungate the last span under the given `feature`. + /// Panics if the given `span` wasn't the last one. + /// + /// Using this is discouraged unless you have a really good reason to. + pub fn ungate_last(&self, feature: Symbol, span: Span) { + let removed_span = self.spans.borrow_mut().entry(feature).or_default().pop().unwrap(); + debug_assert_eq!(span, removed_span); + } + + /// Is the provided `feature` gate ungated currently? + /// + /// Using this is discouraged unless you have a really good reason to. + pub fn is_ungated(&self, feature: Symbol) -> bool { + self.spans.borrow().get(&feature).map_or(true, |spans| spans.is_empty()) + } + + /// Prepend the given set of `spans` onto the set in `self`. + pub fn merge(&self, mut spans: FxHashMap<Symbol, Vec<Span>>) { + let mut inner = self.spans.borrow_mut(); + for (gate, mut gate_spans) in inner.drain() { + spans.entry(gate).or_default().append(&mut gate_spans); + } + *inner = spans; + } +} + +#[derive(Default)] +pub struct SymbolGallery { + /// All symbols occurred and their first occurrence span. + pub symbols: Lock<FxHashMap<Symbol, Span>>, +} + +impl SymbolGallery { + /// Insert a symbol and its span into symbol gallery. + /// If the symbol has occurred before, ignore the new occurance. + pub fn insert(&self, symbol: Symbol, span: Span) { + self.symbols.lock().entry(symbol).or_insert(span); + } +} + +/// Construct a diagnostic for a language feature error due to the given `span`. +/// The `feature`'s `Symbol` is the one you used in `active.rs` and `rustc_span::symbols`. +pub fn feature_err<'a>( + sess: &'a ParseSess, + feature: Symbol, + span: impl Into<MultiSpan>, + explain: &str, +) -> DiagnosticBuilder<'a> { + feature_err_issue(sess, feature, span, GateIssue::Language, explain) +} + +/// Construct a diagnostic for a feature gate error. +/// +/// This variant allows you to control whether it is a library or language feature. +/// Almost always, you want to use this for a language feature. If so, prefer `feature_err`. +pub fn feature_err_issue<'a>( + sess: &'a ParseSess, + feature: Symbol, + span: impl Into<MultiSpan>, + issue: GateIssue, + explain: &str, +) -> DiagnosticBuilder<'a> { + let mut err = sess.span_diagnostic.struct_span_err_with_code(span, explain, error_code!(E0658)); + + if let Some(n) = find_feature_issue(feature, issue) { + err.note(&format!( + "see issue #{} <https://github.com/rust-lang/rust/issues/{}> for more information", + n, n, + )); + } + + // #23973: do not suggest `#![feature(...)]` if we are in beta/stable + if sess.unstable_features.is_nightly_build() { + err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature)); + } + + err +} + +/// Info about a parsing session. +pub struct ParseSess { + pub span_diagnostic: Handler, + pub unstable_features: UnstableFeatures, + pub config: CrateConfig, + pub edition: Edition, + /// Places where raw identifiers were used. This is used for feature-gating raw identifiers. + pub raw_identifier_spans: Lock<Vec<Span>>, + /// Used to determine and report recursive module inclusions. + pub included_mod_stack: Lock<Vec<PathBuf>>, + source_map: Lrc<SourceMap>, + pub buffered_lints: Lock<Vec<BufferedEarlyLint>>, + /// Contains the spans of block expressions that could have been incomplete based on the + /// operation token that followed it, but that the parser cannot identify without further + /// analysis. + pub ambiguous_block_expr_parse: Lock<FxHashMap<Span, Span>>, + pub injected_crate_name: OnceCell<Symbol>, + pub gated_spans: GatedSpans, + pub symbol_gallery: SymbolGallery, + /// The parser has reached `Eof` due to an unclosed brace. Used to silence unnecessary errors. + pub reached_eof: Lock<bool>, + /// Environment variables accessed during the build and their values when they exist. + pub env_depinfo: Lock<FxHashSet<(Symbol, Option<Symbol>)>>, + /// All the type ascriptions expressions that have had a suggestion for likely path typo. + pub type_ascription_path_suggestions: Lock<FxHashSet<Span>>, +} + +impl ParseSess { + pub fn new(file_path_mapping: FilePathMapping) -> Self { + let sm = Lrc::new(SourceMap::new(file_path_mapping)); + let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, None, Some(sm.clone())); + ParseSess::with_span_handler(handler, sm) + } + + pub fn with_span_handler(handler: Handler, source_map: Lrc<SourceMap>) -> Self { + Self { + span_diagnostic: handler, + unstable_features: UnstableFeatures::from_environment(), + config: FxHashSet::default(), + edition: ExpnId::root().expn_data().edition, + raw_identifier_spans: Lock::new(Vec::new()), + included_mod_stack: Lock::new(vec![]), + source_map, + buffered_lints: Lock::new(vec![]), + ambiguous_block_expr_parse: Lock::new(FxHashMap::default()), + injected_crate_name: OnceCell::new(), + gated_spans: GatedSpans::default(), + symbol_gallery: SymbolGallery::default(), + reached_eof: Lock::new(false), + env_depinfo: Default::default(), + type_ascription_path_suggestions: Default::default(), + } + } + + pub fn with_silent_emitter() -> Self { + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let handler = Handler::with_emitter(false, None, Box::new(SilentEmitter)); + ParseSess::with_span_handler(handler, sm) + } + + #[inline] + pub fn source_map(&self) -> &SourceMap { + &self.source_map + } + + pub fn clone_source_map(&self) -> Lrc<SourceMap> { + self.source_map.clone() + } + + pub fn buffer_lint( + &self, + lint: &'static Lint, + span: impl Into<MultiSpan>, + node_id: NodeId, + msg: &str, + ) { + self.buffered_lints.with_lock(|buffered_lints| { + buffered_lints.push(BufferedEarlyLint { + span: span.into(), + node_id, + msg: msg.into(), + lint_id: LintId::of(lint), + diagnostic: BuiltinLintDiagnostics::Normal, + }); + }); + } + + pub fn buffer_lint_with_diagnostic( + &self, + lint: &'static Lint, + span: impl Into<MultiSpan>, + node_id: NodeId, + msg: &str, + diagnostic: BuiltinLintDiagnostics, + ) { + self.buffered_lints.with_lock(|buffered_lints| { + buffered_lints.push(BufferedEarlyLint { + span: span.into(), + node_id, + msg: msg.into(), + lint_id: LintId::of(lint), + diagnostic, + }); + }); + } + + /// Extend an error with a suggestion to wrap an expression with parentheses to allow the + /// parser to continue parsing the following operation as part of the same expression. + pub fn expr_parentheses_needed( + &self, + err: &mut DiagnosticBuilder<'_>, + span: Span, + alt_snippet: Option<String>, + ) { + if let Some(snippet) = self.source_map().span_to_snippet(span).ok().or(alt_snippet) { + err.span_suggestion( + span, + "parentheses are required to parse this as an expression", + format!("({})", snippet), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs new file mode 100644 index 00000000000..e12364b7dac --- /dev/null +++ b/compiler/rustc_session/src/search_paths.rs @@ -0,0 +1,95 @@ +use crate::filesearch::make_target_lib_path; +use crate::{config, early_error}; +use std::path::{Path, PathBuf}; + +#[derive(Clone, Debug)] +pub struct SearchPath { + pub kind: PathKind, + pub dir: PathBuf, + pub files: Vec<SearchPathFile>, +} + +// The obvious implementation of `SearchPath::files` is a `Vec<PathBuf>`. But +// it is searched repeatedly by `find_library_crate`, and the searches involve +// checking the prefix and suffix of the filename of each `PathBuf`. This is +// doable, but very slow, because it involves calls to `file_name` and +// `extension` that are themselves slow. +// +// This type augments the `PathBuf` with an `Option<String>` containing the +// `PathBuf`'s filename. The prefix and suffix checking is much faster on the +// `Option<String>` than the `PathBuf`. (It's an `Option` because +// `Path::file_name` can fail; if that happens then all subsequent checking +// will also fail, which is fine.) +#[derive(Clone, Debug)] +pub struct SearchPathFile { + pub path: PathBuf, + pub file_name_str: Option<String>, +} + +impl SearchPathFile { + fn new(path: PathBuf) -> SearchPathFile { + let file_name_str = path.file_name().and_then(|f| f.to_str()).map(|s| s.to_string()); + SearchPathFile { path, file_name_str } + } +} + +#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Encodable, Decodable)] +pub enum PathKind { + Native, + Crate, + Dependency, + Framework, + ExternFlag, + All, +} + +rustc_data_structures::impl_stable_hash_via_hash!(PathKind); + +impl PathKind { + pub fn matches(&self, kind: PathKind) -> bool { + match (self, kind) { + (PathKind::All, _) | (_, PathKind::All) => true, + _ => *self == kind, + } + } +} + +impl SearchPath { + pub fn from_cli_opt(path: &str, output: config::ErrorOutputType) -> Self { + let (kind, path) = if path.starts_with("native=") { + (PathKind::Native, &path["native=".len()..]) + } else if path.starts_with("crate=") { + (PathKind::Crate, &path["crate=".len()..]) + } else if path.starts_with("dependency=") { + (PathKind::Dependency, &path["dependency=".len()..]) + } else if path.starts_with("framework=") { + (PathKind::Framework, &path["framework=".len()..]) + } else if path.starts_with("all=") { + (PathKind::All, &path["all=".len()..]) + } else { + (PathKind::All, path) + }; + if path.is_empty() { + early_error(output, "empty search path given via `-L`"); + } + + let dir = PathBuf::from(path); + Self::new(kind, dir) + } + + pub fn from_sysroot_and_triple(sysroot: &Path, triple: &str) -> Self { + Self::new(PathKind::All, make_target_lib_path(sysroot, triple)) + } + + fn new(kind: PathKind, dir: PathBuf) -> Self { + // Get the files within the directory. + let files = match std::fs::read_dir(&dir) { + Ok(files) => files + .filter_map(|e| e.ok().map(|e| SearchPathFile::new(e.path()))) + .collect::<Vec<_>>(), + Err(..) => vec![], + }; + + SearchPath { kind, dir, files } + } +} diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs new file mode 100644 index 00000000000..c006e593e47 --- /dev/null +++ b/compiler/rustc_session/src/session.rs @@ -0,0 +1,1539 @@ +use crate::cgu_reuse_tracker::CguReuseTracker; +use crate::code_stats::CodeStats; +pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; +use crate::config::{self, CrateType, OutputType, PrintRequest, SanitizerSet, SwitchWithOptPath}; +use crate::filesearch; +use crate::lint; +use crate::parse::ParseSess; +use crate::search_paths::{PathKind, SearchPath}; + +pub use rustc_ast::attr::MarkedAttrs; +pub use rustc_ast::crate_disambiguator::CrateDisambiguator; +pub use rustc_ast::Attribute; +use rustc_data_structures::flock; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::jobserver::{self, Client}; +use rustc_data_structures::profiling::{duration_to_secs_str, SelfProfiler, SelfProfilerRef}; +use rustc_data_structures::sync::{ + self, AtomicU64, AtomicUsize, Lock, Lrc, OnceCell, OneThread, Ordering, Ordering::SeqCst, +}; +use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter; +use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType}; +use rustc_errors::json::JsonEmitter; +use rustc_errors::registry::Registry; +use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported}; +use rustc_span::edition::Edition; +use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span}; +use rustc_span::{sym, SourceFileHashAlgorithm, Symbol}; +use rustc_target::asm::InlineAsmArch; +use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel}; +use rustc_target::spec::{Target, TargetTriple, TlsModel}; + +use std::cell::{self, RefCell}; +use std::env; +use std::fmt; +use std::io::Write; +use std::num::NonZeroU32; +use std::ops::{Div, Mul}; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::Arc; +use std::time::Duration; + +pub struct OptimizationFuel { + /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`. + remaining: u64, + /// We're rejecting all further optimizations. + out_of_fuel: bool, +} + +/// The behavior of the CTFE engine when an error occurs with regards to backtraces. +#[derive(Clone, Copy)] +pub enum CtfeBacktrace { + /// Do nothing special, return the error as usual without a backtrace. + Disabled, + /// Capture a backtrace at the point the error is created and return it in the error + /// (to be printed later if/when the error ever actually gets shown to the user). + Capture, + /// Capture a backtrace at the point the error is created and immediately print it out. + Immediate, +} + +/// New-type wrapper around `usize` for representing limits. Ensures that comparisons against +/// limits are consistent throughout the compiler. +#[derive(Clone, Copy, Debug)] +pub struct Limit(pub usize); + +impl Limit { + /// Create a new limit from a `usize`. + pub fn new(value: usize) -> Self { + Limit(value) + } + + /// Check that `value` is within the limit. Ensures that the same comparisons are used + /// throughout the compiler, as mismatches can cause ICEs, see #72540. + pub fn value_within_limit(&self, value: usize) -> bool { + value <= self.0 + } +} + +impl fmt::Display for Limit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Div<usize> for Limit { + type Output = Limit; + + fn div(self, rhs: usize) -> Self::Output { + Limit::new(self.0 / rhs) + } +} + +impl Mul<usize> for Limit { + type Output = Limit; + + fn mul(self, rhs: usize) -> Self::Output { + Limit::new(self.0 * rhs) + } +} + +/// Represents the data associated with a compilation +/// session for a single crate. +pub struct Session { + pub target: config::Config, + pub host: Target, + pub opts: config::Options, + pub host_tlib_path: SearchPath, + /// `None` if the host and target are the same. + pub target_tlib_path: Option<SearchPath>, + pub parse_sess: ParseSess, + pub sysroot: PathBuf, + /// The name of the root source file of the crate, in the local file system. + /// `None` means that there is no source file. + pub local_crate_source_file: Option<PathBuf>, + /// The directory the compiler has been executed in plus a flag indicating + /// if the value stored here has been affected by path remapping. + pub working_dir: (PathBuf, bool), + + /// Set of `(DiagnosticId, Option<Span>, message)` tuples tracking + /// (sub)diagnostics that have been set once, but should not be set again, + /// in order to avoid redundantly verbose output (Issue #24690, #44953). + pub one_time_diagnostics: Lock<FxHashSet<(DiagnosticMessageId, Option<Span>, String)>>, + crate_types: OnceCell<Vec<CrateType>>, + /// The `crate_disambiguator` is constructed out of all the `-C metadata` + /// arguments passed to the compiler. Its value together with the crate-name + /// forms a unique global identifier for the crate. It is used to allow + /// multiple crates with the same name to coexist. See the + /// `rustc_codegen_llvm::back::symbol_names` module for more information. + pub crate_disambiguator: OnceCell<CrateDisambiguator>, + + features: OnceCell<rustc_feature::Features>, + + /// The maximum recursion limit for potentially infinitely recursive + /// operations such as auto-dereference and monomorphization. + pub recursion_limit: OnceCell<Limit>, + + /// The maximum length of types during monomorphization. + pub type_length_limit: OnceCell<Limit>, + + /// The maximum blocks a const expression can evaluate. + pub const_eval_limit: OnceCell<Limit>, + + incr_comp_session: OneThread<RefCell<IncrCompSession>>, + /// Used for incremental compilation tests. Will only be populated if + /// `-Zquery-dep-graph` is specified. + pub cgu_reuse_tracker: CguReuseTracker, + + /// Used by `-Z self-profile`. + pub prof: SelfProfilerRef, + + /// Some measurements that are being gathered during compilation. + pub perf_stats: PerfStats, + + /// Data about code being compiled, gathered during compilation. + pub code_stats: CodeStats, + + /// If `-zfuel=crate=n` is specified, `Some(crate)`. + optimization_fuel_crate: Option<String>, + + /// Tracks fuel info if `-zfuel=crate=n` is specified. + optimization_fuel: Lock<OptimizationFuel>, + + // The next two are public because the driver needs to read them. + /// If `-zprint-fuel=crate`, `Some(crate)`. + pub print_fuel_crate: Option<String>, + /// Always set to zero and incremented so that we can print fuel expended by a crate. + pub print_fuel: AtomicU64, + + /// Loaded up early on in the initialization of this `Session` to avoid + /// false positives about a job server in our environment. + pub jobserver: Client, + + /// Cap lint level specified by a driver specifically. + pub driver_lint_caps: FxHashMap<lint::LintId, lint::Level>, + + /// `Span`s of trait methods that weren't found to avoid emitting object safety errors + pub trait_methods_not_found: Lock<FxHashSet<Span>>, + + /// Mapping from ident span to path span for paths that don't exist as written, but that + /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. + pub confused_type_with_std_module: Lock<FxHashMap<Span, Span>>, + + /// Path for libraries that will take preference over libraries shipped by Rust. + /// Used by windows-gnu targets to priortize system mingw-w64 libraries. + pub system_library_path: OneThread<RefCell<Option<Option<PathBuf>>>>, + + /// Tracks the current behavior of the CTFE engine when an error occurs. + /// Options range from returning the error without a backtrace to returning an error + /// and immediately printing the backtrace to stderr. + pub ctfe_backtrace: Lock<CtfeBacktrace>, + + /// This tracks where `-Zunleash-the-miri-inside-of-you` was used to get around a + /// const check, optionally with the relevant feature gate. We use this to + /// warn about unleashing, but with a single diagnostic instead of dozens that + /// drown everything else in noise. + miri_unleashed_features: Lock<Vec<(Span, Option<Symbol>)>>, + + /// Base directory containing the `src/` for the Rust standard library, and + /// potentially `rustc` as well, if we can can find it. Right now it's always + /// `$sysroot/lib/rustlib/src/rust` (i.e. the `rustup` `rust-src` component). + /// + /// This directory is what the virtual `/rustc/$hash` is translated back to, + /// if Rust was built with path remapping to `/rustc/$hash` enabled + /// (the `rust.remap-debuginfo` option in `config.toml`). + pub real_rust_source_base_dir: Option<PathBuf>, + + /// Architecture to use for interpreting asm!. + pub asm_arch: Option<InlineAsmArch>, + + /// Set of enabled features for the current target. + pub target_features: FxHashSet<Symbol>, + + known_attrs: Lock<MarkedAttrs>, + used_attrs: Lock<MarkedAttrs>, +} + +pub struct PerfStats { + /// The accumulated time spent on computing symbol hashes. + pub symbol_hash_time: Lock<Duration>, + /// Total number of values canonicalized queries constructed. + pub queries_canonicalized: AtomicUsize, + /// Number of times this query is invoked. + pub normalize_generic_arg_after_erasing_regions: AtomicUsize, + /// Number of times this query is invoked. + pub normalize_projection_ty: AtomicUsize, +} + +/// Enum to support dispatch of one-time diagnostics (in `Session.diag_once`). +enum DiagnosticBuilderMethod { + Note, + SpanNote, + SpanSuggestion(String), // suggestion + // Add more variants as needed to support one-time diagnostics. +} + +/// Diagnostic message ID, used by `Session.one_time_diagnostics` to avoid +/// emitting the same message more than once. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum DiagnosticMessageId { + ErrorId(u16), // EXXXX error code as integer + LintId(lint::LintId), + StabilityId(Option<NonZeroU32>), // issue number +} + +impl From<&'static lint::Lint> for DiagnosticMessageId { + fn from(lint: &'static lint::Lint) -> Self { + DiagnosticMessageId::LintId(lint::LintId::of(lint)) + } +} + +impl Session { + pub fn miri_unleashed_feature(&self, span: Span, feature_gate: Option<Symbol>) { + self.miri_unleashed_features.lock().push((span, feature_gate)); + } + + fn check_miri_unleashed_features(&self) { + let unleashed_features = self.miri_unleashed_features.lock(); + if !unleashed_features.is_empty() { + let mut must_err = false; + // Create a diagnostic pointing at where things got unleashed. + let mut diag = self.struct_warn("skipping const checks"); + for &(span, feature_gate) in unleashed_features.iter() { + // FIXME: `span_label` doesn't do anything, so we use "help" as a hack. + if let Some(feature_gate) = feature_gate { + diag.span_help(span, &format!("skipping check for `{}` feature", feature_gate)); + // The unleash flag must *not* be used to just "hack around" feature gates. + must_err = true; + } else { + diag.span_help(span, "skipping check that does not even have a feature gate"); + } + } + diag.emit(); + // If we should err, make sure we did. + if must_err && !self.has_errors() { + // We have skipped a feature gate, and not run into other errors... reject. + self.err( + "`-Zunleash-the-miri-inside-of-you` may not be used to circumvent feature \ + gates, except when testing error paths in the CTFE engine", + ); + } + } + } + + /// Invoked all the way at the end to finish off diagnostics printing. + pub fn finish_diagnostics(&self, registry: &Registry) { + self.check_miri_unleashed_features(); + self.diagnostic().print_error_count(registry); + } + + pub fn local_crate_disambiguator(&self) -> CrateDisambiguator { + self.crate_disambiguator.get().copied().unwrap() + } + + pub fn crate_types(&self) -> &[CrateType] { + self.crate_types.get().unwrap().as_slice() + } + + pub fn init_crate_types(&self, crate_types: Vec<CrateType>) { + self.crate_types.set(crate_types).expect("`crate_types` was initialized twice") + } + + pub fn recursion_limit(&self) -> Limit { + self.recursion_limit.get().copied().unwrap() + } + + pub fn type_length_limit(&self) -> Limit { + self.type_length_limit.get().copied().unwrap() + } + + pub fn const_eval_limit(&self) -> Limit { + self.const_eval_limit.get().copied().unwrap() + } + + pub fn struct_span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_warn(sp, msg) + } + pub fn struct_span_warn_with_code<S: Into<MultiSpan>>( + &self, + sp: S, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_warn_with_code(sp, msg, code) + } + pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_warn(msg) + } + pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_err(sp, msg) + } + pub fn struct_span_err_with_code<S: Into<MultiSpan>>( + &self, + sp: S, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_err_with_code(sp, msg, code) + } + // FIXME: This method should be removed (every error should have an associated error code). + pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_err(msg) + } + pub fn struct_err_with_code(&self, msg: &str, code: DiagnosticId) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_err_with_code(msg, code) + } + pub fn struct_span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_fatal(sp, msg) + } + pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>( + &self, + sp: S, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_fatal_with_code(sp, msg, code) + } + pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_fatal(msg) + } + + pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! { + self.diagnostic().span_fatal(sp, msg).raise() + } + pub fn span_fatal_with_code<S: Into<MultiSpan>>( + &self, + sp: S, + msg: &str, + code: DiagnosticId, + ) -> ! { + self.diagnostic().span_fatal_with_code(sp, msg, code).raise() + } + pub fn fatal(&self, msg: &str) -> ! { + self.diagnostic().fatal(msg).raise() + } + pub fn span_err_or_warn<S: Into<MultiSpan>>(&self, is_warning: bool, sp: S, msg: &str) { + if is_warning { + self.span_warn(sp, msg); + } else { + self.span_err(sp, msg); + } + } + pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { + self.diagnostic().span_err(sp, msg) + } + pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { + self.diagnostic().span_err_with_code(sp, &msg, code) + } + pub fn err(&self, msg: &str) { + self.diagnostic().err(msg) + } + pub fn err_count(&self) -> usize { + self.diagnostic().err_count() + } + pub fn has_errors(&self) -> bool { + self.diagnostic().has_errors() + } + pub fn has_errors_or_delayed_span_bugs(&self) -> bool { + self.diagnostic().has_errors_or_delayed_span_bugs() + } + pub fn abort_if_errors(&self) { + self.diagnostic().abort_if_errors(); + } + pub fn compile_status(&self) -> Result<(), ErrorReported> { + if self.has_errors() { + self.diagnostic().emit_stashed_diagnostics(); + Err(ErrorReported) + } else { + Ok(()) + } + } + // FIXME(matthewjasper) Remove this method, it should never be needed. + pub fn track_errors<F, T>(&self, f: F) -> Result<T, ErrorReported> + where + F: FnOnce() -> T, + { + let old_count = self.err_count(); + let result = f(); + let errors = self.err_count() - old_count; + if errors == 0 { Ok(result) } else { Err(ErrorReported) } + } + pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { + self.diagnostic().span_warn(sp, msg) + } + pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { + self.diagnostic().span_warn_with_code(sp, msg, code) + } + pub fn warn(&self, msg: &str) { + self.diagnostic().warn(msg) + } + pub fn opt_span_warn<S: Into<MultiSpan>>(&self, opt_sp: Option<S>, msg: &str) { + match opt_sp { + Some(sp) => self.span_warn(sp, msg), + None => self.warn(msg), + } + } + /// Delay a span_bug() call until abort_if_errors() + #[track_caller] + pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { + self.diagnostic().delay_span_bug(sp, msg) + } + pub fn note_without_error(&self, msg: &str) { + self.diagnostic().note_without_error(msg) + } + pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { + self.diagnostic().span_note_without_error(sp, msg) + } + pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_note_without_error(msg) + } + + pub fn diagnostic(&self) -> &rustc_errors::Handler { + &self.parse_sess.span_diagnostic + } + + /// Analogous to calling methods on the given `DiagnosticBuilder`, but + /// deduplicates on lint ID, span (if any), and message for this `Session` + fn diag_once<'a, 'b>( + &'a self, + diag_builder: &'b mut DiagnosticBuilder<'a>, + method: DiagnosticBuilderMethod, + msg_id: DiagnosticMessageId, + message: &str, + span_maybe: Option<Span>, + ) { + let id_span_message = (msg_id, span_maybe, message.to_owned()); + let fresh = self.one_time_diagnostics.borrow_mut().insert(id_span_message); + if fresh { + match method { + DiagnosticBuilderMethod::Note => { + diag_builder.note(message); + } + DiagnosticBuilderMethod::SpanNote => { + let span = span_maybe.expect("`span_note` needs a span"); + diag_builder.span_note(span, message); + } + DiagnosticBuilderMethod::SpanSuggestion(suggestion) => { + let span = span_maybe.expect("`span_suggestion_*` needs a span"); + diag_builder.span_suggestion( + span, + message, + suggestion, + Applicability::Unspecified, + ); + } + } + } + } + + pub fn diag_span_note_once<'a, 'b>( + &'a self, + diag_builder: &'b mut DiagnosticBuilder<'a>, + msg_id: DiagnosticMessageId, + span: Span, + message: &str, + ) { + self.diag_once( + diag_builder, + DiagnosticBuilderMethod::SpanNote, + msg_id, + message, + Some(span), + ); + } + + pub fn diag_note_once<'a, 'b>( + &'a self, + diag_builder: &'b mut DiagnosticBuilder<'a>, + msg_id: DiagnosticMessageId, + message: &str, + ) { + self.diag_once(diag_builder, DiagnosticBuilderMethod::Note, msg_id, message, None); + } + + pub fn diag_span_suggestion_once<'a, 'b>( + &'a self, + diag_builder: &'b mut DiagnosticBuilder<'a>, + msg_id: DiagnosticMessageId, + span: Span, + message: &str, + suggestion: String, + ) { + self.diag_once( + diag_builder, + DiagnosticBuilderMethod::SpanSuggestion(suggestion), + msg_id, + message, + Some(span), + ); + } + + #[inline] + pub fn source_map(&self) -> &SourceMap { + self.parse_sess.source_map() + } + pub fn verbose(&self) -> bool { + self.opts.debugging_opts.verbose + } + pub fn time_passes(&self) -> bool { + self.opts.debugging_opts.time_passes || self.opts.debugging_opts.time + } + pub fn instrument_mcount(&self) -> bool { + self.opts.debugging_opts.instrument_mcount + } + pub fn time_llvm_passes(&self) -> bool { + self.opts.debugging_opts.time_llvm_passes + } + pub fn meta_stats(&self) -> bool { + self.opts.debugging_opts.meta_stats + } + pub fn asm_comments(&self) -> bool { + self.opts.debugging_opts.asm_comments + } + pub fn verify_llvm_ir(&self) -> bool { + self.opts.debugging_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() + } + pub fn borrowck_stats(&self) -> bool { + self.opts.debugging_opts.borrowck_stats + } + pub fn print_llvm_passes(&self) -> bool { + self.opts.debugging_opts.print_llvm_passes + } + pub fn binary_dep_depinfo(&self) -> bool { + self.opts.debugging_opts.binary_dep_depinfo + } + + /// Gets the features enabled for the current compilation session. + /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents + /// dependency tracking. Use tcx.features() instead. + #[inline] + pub fn features_untracked(&self) -> &rustc_feature::Features { + self.features.get().unwrap() + } + + pub fn init_features(&self, features: rustc_feature::Features) { + match self.features.set(features) { + Ok(()) => {} + Err(_) => panic!("`features` was initialized twice"), + } + } + + /// Calculates the flavor of LTO to use for this compilation. + pub fn lto(&self) -> config::Lto { + // If our target has codegen requirements ignore the command line + if self.target.target.options.requires_lto { + return config::Lto::Fat; + } + + // If the user specified something, return that. If they only said `-C + // lto` and we've for whatever reason forced off ThinLTO via the CLI, + // then ensure we can't use a ThinLTO. + match self.opts.cg.lto { + config::LtoCli::Unspecified => { + // The compiler was invoked without the `-Clto` flag. Fall + // through to the default handling + } + config::LtoCli::No => { + // The user explicitly opted out of any kind of LTO + return config::Lto::No; + } + config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => { + // All of these mean fat LTO + return config::Lto::Fat; + } + config::LtoCli::Thin => { + return if self.opts.cli_forced_thinlto_off { + config::Lto::Fat + } else { + config::Lto::Thin + }; + } + } + + // Ok at this point the target doesn't require anything and the user + // hasn't asked for anything. Our next decision is whether or not + // we enable "auto" ThinLTO where we use multiple codegen units and + // then do ThinLTO over those codegen units. The logic below will + // either return `No` or `ThinLocal`. + + // If processing command line options determined that we're incompatible + // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option. + if self.opts.cli_forced_thinlto_off { + return config::Lto::No; + } + + // If `-Z thinlto` specified process that, but note that this is mostly + // a deprecated option now that `-C lto=thin` exists. + if let Some(enabled) = self.opts.debugging_opts.thinlto { + if enabled { + return config::Lto::ThinLocal; + } else { + return config::Lto::No; + } + } + + // If there's only one codegen unit and LTO isn't enabled then there's + // no need for ThinLTO so just return false. + if self.codegen_units() == 1 { + return config::Lto::No; + } + + // Now we're in "defaults" territory. By default we enable ThinLTO for + // optimized compiles (anything greater than O0). + match self.opts.optimize { + config::OptLevel::No => config::Lto::No, + _ => config::Lto::ThinLocal, + } + } + + /// Returns the panic strategy for this compile session. If the user explicitly selected one + /// using '-C panic', use that, otherwise use the panic strategy defined by the target. + pub fn panic_strategy(&self) -> PanicStrategy { + self.opts.cg.panic.unwrap_or(self.target.target.options.panic_strategy) + } + pub fn fewer_names(&self) -> bool { + let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly) + || self.opts.output_types.contains_key(&OutputType::Bitcode) + // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue. + || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY); + + self.opts.debugging_opts.fewer_names || !more_names + } + + pub fn unstable_options(&self) -> bool { + self.opts.debugging_opts.unstable_options + } + pub fn overflow_checks(&self) -> bool { + self.opts + .cg + .overflow_checks + .or(self.opts.debugging_opts.force_overflow_checks) + .unwrap_or(self.opts.debug_assertions) + } + + /// Check whether this compile session and crate type use static crt. + pub fn crt_static(&self, crate_type: Option<CrateType>) -> bool { + if !self.target.target.options.crt_static_respected { + // If the target does not opt in to crt-static support, use its default. + return self.target.target.options.crt_static_default; + } + + let requested_features = self.opts.cg.target_feature.split(','); + let found_negative = requested_features.clone().any(|r| r == "-crt-static"); + let found_positive = requested_features.clone().any(|r| r == "+crt-static"); + + if found_positive || found_negative { + found_positive + } else if crate_type == Some(CrateType::ProcMacro) + || crate_type == None && self.opts.crate_types.contains(&CrateType::ProcMacro) + { + // FIXME: When crate_type is not available, + // we use compiler options to determine the crate_type. + // We can't check `#![crate_type = "proc-macro"]` here. + false + } else { + self.target.target.options.crt_static_default + } + } + + pub fn relocation_model(&self) -> RelocModel { + self.opts.cg.relocation_model.unwrap_or(self.target.target.options.relocation_model) + } + + pub fn code_model(&self) -> Option<CodeModel> { + self.opts.cg.code_model.or(self.target.target.options.code_model) + } + + pub fn tls_model(&self) -> TlsModel { + self.opts.debugging_opts.tls_model.unwrap_or(self.target.target.options.tls_model) + } + + pub fn must_not_eliminate_frame_pointers(&self) -> bool { + // "mcount" function relies on stack pointer. + // See <https://sourceware.org/binutils/docs/gprof/Implementation.html>. + if self.instrument_mcount() { + true + } else if let Some(x) = self.opts.cg.force_frame_pointers { + x + } else { + !self.target.target.options.eliminate_frame_pointer + } + } + + pub fn must_emit_unwind_tables(&self) -> bool { + // This is used to control the emission of the `uwtable` attribute on + // LLVM functions. + // + // At the very least, unwind tables are needed when compiling with + // `-C panic=unwind`. + // + // On some targets (including windows), however, exceptions include + // other events such as illegal instructions, segfaults, etc. This means + // that on Windows we end up still needing unwind tables even if the `-C + // panic=abort` flag is passed. + // + // You can also find more info on why Windows needs unwind tables in: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 + // + // If a target requires unwind tables, then they must be emitted. + // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>` + // value, if it is provided, or disable them, if not. + if self.panic_strategy() == PanicStrategy::Unwind { + true + } else if self.target.target.options.requires_uwtable { + true + } else { + self.opts.cg.force_unwind_tables.unwrap_or(false) + } + } + + /// Returns the symbol name for the registrar function, + /// given the crate `Svh` and the function `DefIndex`. + pub fn generate_plugin_registrar_symbol(&self, disambiguator: CrateDisambiguator) -> String { + format!("__rustc_plugin_registrar_{}__", disambiguator.to_fingerprint().to_hex()) + } + + pub fn generate_proc_macro_decls_symbol(&self, disambiguator: CrateDisambiguator) -> String { + format!("__rustc_proc_macro_decls_{}__", disambiguator.to_fingerprint().to_hex()) + } + + pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { + filesearch::FileSearch::new( + &self.sysroot, + self.opts.target_triple.triple(), + &self.opts.search_paths, + // `target_tlib_path == None` means it's the same as `host_tlib_path`. + self.target_tlib_path.as_ref().unwrap_or(&self.host_tlib_path), + kind, + ) + } + pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { + filesearch::FileSearch::new( + &self.sysroot, + config::host_triple(), + &self.opts.search_paths, + &self.host_tlib_path, + kind, + ) + } + + pub fn set_incr_session_load_dep_graph(&self, load: bool) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::Active { ref mut load_dep_graph, .. } = *incr_comp_session { + *load_dep_graph = load; + } + } + + pub fn incr_session_load_dep_graph(&self) -> bool { + let incr_comp_session = self.incr_comp_session.borrow(); + match *incr_comp_session { + IncrCompSession::Active { load_dep_graph, .. } => load_dep_graph, + _ => false, + } + } + + pub fn init_incr_comp_session( + &self, + session_dir: PathBuf, + lock_file: flock::Lock, + load_dep_graph: bool, + ) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::NotInitialized = *incr_comp_session { + } else { + panic!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session) + } + + *incr_comp_session = + IncrCompSession::Active { session_directory: session_dir, lock_file, load_dep_graph }; + } + + pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::Active { .. } = *incr_comp_session { + } else { + panic!("trying to finalize `IncrCompSession` `{:?}`", *incr_comp_session); + } + + // Note: this will also drop the lock file, thus unlocking the directory. + *incr_comp_session = IncrCompSession::Finalized { session_directory: new_directory_path }; + } + + pub fn mark_incr_comp_session_as_invalid(&self) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + let session_directory = match *incr_comp_session { + IncrCompSession::Active { ref session_directory, .. } => session_directory.clone(), + IncrCompSession::InvalidBecauseOfErrors { .. } => return, + _ => panic!("trying to invalidate `IncrCompSession` `{:?}`", *incr_comp_session), + }; + + // Note: this will also drop the lock file, thus unlocking the directory. + *incr_comp_session = IncrCompSession::InvalidBecauseOfErrors { session_directory }; + } + + pub fn incr_comp_session_dir(&self) -> cell::Ref<'_, PathBuf> { + let incr_comp_session = self.incr_comp_session.borrow(); + cell::Ref::map(incr_comp_session, |incr_comp_session| match *incr_comp_session { + IncrCompSession::NotInitialized => panic!( + "trying to get session directory from `IncrCompSession`: {:?}", + *incr_comp_session, + ), + IncrCompSession::Active { ref session_directory, .. } + | IncrCompSession::Finalized { ref session_directory } + | IncrCompSession::InvalidBecauseOfErrors { ref session_directory } => { + session_directory + } + }) + } + + pub fn incr_comp_session_dir_opt(&self) -> Option<cell::Ref<'_, PathBuf>> { + self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir()) + } + + pub fn print_perf_stats(&self) { + println!( + "Total time spent computing symbol hashes: {}", + duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock()) + ); + println!( + "Total queries canonicalized: {}", + self.perf_stats.queries_canonicalized.load(Ordering::Relaxed) + ); + println!( + "normalize_generic_arg_after_erasing_regions: {}", + self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed) + ); + println!( + "normalize_projection_ty: {}", + self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed) + ); + } + + /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. + /// This expends fuel if applicable, and records fuel if applicable. + pub fn consider_optimizing<T: Fn() -> String>(&self, crate_name: &str, msg: T) -> bool { + let mut ret = true; + if let Some(ref c) = self.optimization_fuel_crate { + if c == crate_name { + assert_eq!(self.threads(), 1); + let mut fuel = self.optimization_fuel.lock(); + ret = fuel.remaining != 0; + if fuel.remaining == 0 && !fuel.out_of_fuel { + self.warn(&format!("optimization-fuel-exhausted: {}", msg())); + fuel.out_of_fuel = true; + } else if fuel.remaining > 0 { + fuel.remaining -= 1; + } + } + } + if let Some(ref c) = self.print_fuel_crate { + if c == crate_name { + assert_eq!(self.threads(), 1); + self.print_fuel.fetch_add(1, SeqCst); + } + } + ret + } + + /// Returns the number of query threads that should be used for this + /// compilation + pub fn threads(&self) -> usize { + self.opts.debugging_opts.threads + } + + /// Returns the number of codegen units that should be used for this + /// compilation + pub fn codegen_units(&self) -> usize { + if let Some(n) = self.opts.cli_forced_codegen_units { + return n; + } + if let Some(n) = self.target.target.options.default_codegen_units { + return n as usize; + } + + // If incremental compilation is turned on, we default to a high number + // codegen units in order to reduce the "collateral damage" small + // changes cause. + if self.opts.incremental.is_some() { + return 256; + } + + // Why is 16 codegen units the default all the time? + // + // The main reason for enabling multiple codegen units by default is to + // leverage the ability for the codegen backend to do codegen and + // optimization in parallel. This allows us, especially for large crates, to + // make good use of all available resources on the machine once we've + // hit that stage of compilation. Large crates especially then often + // take a long time in codegen/optimization and this helps us amortize that + // cost. + // + // Note that a high number here doesn't mean that we'll be spawning a + // large number of threads in parallel. The backend of rustc contains + // global rate limiting through the `jobserver` crate so we'll never + // overload the system with too much work, but rather we'll only be + // optimizing when we're otherwise cooperating with other instances of + // rustc. + // + // Rather a high number here means that we should be able to keep a lot + // of idle cpus busy. By ensuring that no codegen unit takes *too* long + // to build we'll be guaranteed that all cpus will finish pretty closely + // to one another and we should make relatively optimal use of system + // resources + // + // Note that the main cost of codegen units is that it prevents LLVM + // from inlining across codegen units. Users in general don't have a lot + // of control over how codegen units are split up so it's our job in the + // compiler to ensure that undue performance isn't lost when using + // codegen units (aka we can't require everyone to slap `#[inline]` on + // everything). + // + // If we're compiling at `-O0` then the number doesn't really matter too + // much because performance doesn't matter and inlining is ok to lose. + // In debug mode we just want to try to guarantee that no cpu is stuck + // doing work that could otherwise be farmed to others. + // + // In release mode, however (O1 and above) performance does indeed + // matter! To recover the loss in performance due to inlining we'll be + // enabling ThinLTO by default (the function for which is just below). + // This will ensure that we recover any inlining wins we otherwise lost + // through codegen unit partitioning. + // + // --- + // + // Ok that's a lot of words but the basic tl;dr; is that we want a high + // number here -- but not too high. Additionally we're "safe" to have it + // always at the same number at all optimization levels. + // + // As a result 16 was chosen here! Mostly because it was a power of 2 + // and most benchmarks agreed it was roughly a local optimum. Not very + // scientific. + 16 + } + + pub fn teach(&self, code: &DiagnosticId) -> bool { + self.opts.debugging_opts.teach && self.diagnostic().must_teach(code) + } + + pub fn rust_2015(&self) -> bool { + self.opts.edition == Edition::Edition2015 + } + + /// Are we allowed to use features from the Rust 2018 edition? + pub fn rust_2018(&self) -> bool { + self.opts.edition >= Edition::Edition2018 + } + + pub fn edition(&self) -> Edition { + self.opts.edition + } + + /// Returns `true` if we cannot skip the PLT for shared library calls. + pub fn needs_plt(&self) -> bool { + // Check if the current target usually needs PLT to be enabled. + // The user can use the command line flag to override it. + let needs_plt = self.target.target.options.needs_plt; + + let dbg_opts = &self.opts.debugging_opts; + + let relro_level = dbg_opts.relro_level.unwrap_or(self.target.target.options.relro_level); + + // Only enable this optimization by default if full relro is also enabled. + // In this case, lazy binding was already unavailable, so nothing is lost. + // This also ensures `-Wl,-z,now` is supported by the linker. + let full_relro = RelroLevel::Full == relro_level; + + // If user didn't explicitly forced us to use / skip the PLT, + // then try to skip it where possible. + dbg_opts.plt.unwrap_or(needs_plt || !full_relro) + } + + /// Checks if LLVM lifetime markers should be emitted. + pub fn emit_lifetime_markers(&self) -> bool { + self.opts.optimize != config::OptLevel::No + // AddressSanitizer uses lifetimes to detect use after scope bugs. + // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. + || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY) + } + + pub fn mark_attr_known(&self, attr: &Attribute) { + self.known_attrs.lock().mark(attr) + } + + pub fn is_attr_known(&self, attr: &Attribute) -> bool { + self.known_attrs.lock().is_marked(attr) + } + + pub fn mark_attr_used(&self, attr: &Attribute) { + self.used_attrs.lock().mark(attr) + } + + pub fn is_attr_used(&self, attr: &Attribute) -> bool { + self.used_attrs.lock().is_marked(attr) + } + + /// Returns `true` if the attribute's path matches the argument. If it matches, then the + /// attribute is marked as used. + + /// Returns `true` if the attribute's path matches the argument. If it + /// matches, then the attribute is marked as used. + /// + /// This method should only be used by rustc, other tools can use + /// `Attribute::has_name` instead, because only rustc is supposed to report + /// the `unused_attributes` lint. (`MetaItem` and `NestedMetaItem` are + /// produced by lowering an `Attribute` and don't have identity, so they + /// only have the `has_name` method, and you need to mark the original + /// `Attribute` as used when necessary.) + pub fn check_name(&self, attr: &Attribute, name: Symbol) -> bool { + let matches = attr.has_name(name); + if matches { + self.mark_attr_used(attr); + } + matches + } + + pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] + .iter() + .any(|kind| self.check_name(attr, *kind)) + } + + pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { + attrs.iter().any(|item| self.check_name(item, name)) + } + + pub fn find_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> Option<&'a Attribute> { + attrs.iter().find(|attr| self.check_name(attr, name)) + } + + pub fn filter_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> impl Iterator<Item = &'a Attribute> { + attrs.iter().filter(move |attr| self.check_name(attr, name)) + } + + pub fn first_attr_value_str_by_name( + &self, + attrs: &[Attribute], + name: Symbol, + ) -> Option<Symbol> { + attrs.iter().find(|at| self.check_name(at, name)).and_then(|at| at.value_str()) + } +} + +fn default_emitter( + sopts: &config::Options, + registry: rustc_errors::registry::Registry, + source_map: Lrc<SourceMap>, + emitter_dest: Option<Box<dyn Write + Send>>, +) -> Box<dyn Emitter + sync::Send> { + let macro_backtrace = sopts.debugging_opts.macro_backtrace; + match (sopts.error_format, emitter_dest) { + (config::ErrorOutputType::HumanReadable(kind), dst) => { + let (short, color_config) = kind.unzip(); + + if let HumanReadableErrorType::AnnotateSnippet(_) = kind { + let emitter = + AnnotateSnippetEmitterWriter::new(Some(source_map), short, macro_backtrace); + Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) + } else { + let emitter = match dst { + None => EmitterWriter::stderr( + color_config, + Some(source_map), + short, + sopts.debugging_opts.teach, + sopts.debugging_opts.terminal_width, + macro_backtrace, + ), + Some(dst) => EmitterWriter::new( + dst, + Some(source_map), + short, + false, // no teach messages when writing to a buffer + false, // no colors when writing to a buffer + None, // no terminal width + macro_backtrace, + ), + }; + Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) + } + } + (config::ErrorOutputType::Json { pretty, json_rendered }, None) => Box::new( + JsonEmitter::stderr( + Some(registry), + source_map, + pretty, + json_rendered, + sopts.debugging_opts.terminal_width, + macro_backtrace, + ) + .ui_testing(sopts.debugging_opts.ui_testing), + ), + (config::ErrorOutputType::Json { pretty, json_rendered }, Some(dst)) => Box::new( + JsonEmitter::new( + dst, + Some(registry), + source_map, + pretty, + json_rendered, + sopts.debugging_opts.terminal_width, + macro_backtrace, + ) + .ui_testing(sopts.debugging_opts.ui_testing), + ), + } +} + +pub enum DiagnosticOutput { + Default, + Raw(Box<dyn Write + Send>), +} + +pub fn build_session( + sopts: config::Options, + local_crate_source_file: Option<PathBuf>, + registry: rustc_errors::registry::Registry, + diagnostics_output: DiagnosticOutput, + driver_lint_caps: FxHashMap<lint::LintId, lint::Level>, + file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>, +) -> Session { + // FIXME: This is not general enough to make the warning lint completely override + // normal diagnostic warnings, since the warning lint can also be denied and changed + // later via the source code. + let warnings_allow = sopts + .lint_opts + .iter() + .filter(|&&(ref key, _)| *key == "warnings") + .map(|&(_, ref level)| *level == lint::Allow) + .last() + .unwrap_or(false); + let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow); + let can_emit_warnings = !(warnings_allow || cap_lints_allow); + + let write_dest = match diagnostics_output { + DiagnosticOutput::Default => None, + DiagnosticOutput::Raw(write) => Some(write), + }; + + let target_cfg = config::build_target_config(&sopts, sopts.error_format); + let host_triple = TargetTriple::from_triple(config::host_triple()); + let host = Target::search(&host_triple).unwrap_or_else(|e| { + early_error(sopts.error_format, &format!("Error loading host specification: {}", e)) + }); + + let loader = file_loader.unwrap_or(Box::new(RealFileLoader)); + let hash_kind = sopts.debugging_opts.src_hash_algorithm.unwrap_or_else(|| { + if target_cfg.target.options.is_like_msvc { + SourceFileHashAlgorithm::Sha1 + } else { + SourceFileHashAlgorithm::Md5 + } + }); + let source_map = Lrc::new(SourceMap::with_file_loader_and_hash_kind( + loader, + sopts.file_path_mapping(), + hash_kind, + )); + let emitter = default_emitter(&sopts, registry, source_map.clone(), write_dest); + + let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags( + emitter, + sopts.debugging_opts.diagnostic_handler_flags(can_emit_warnings), + ); + + let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.debugging_opts.self_profile + { + let directory = + if let Some(ref directory) = d { directory } else { std::path::Path::new(".") }; + + let profiler = SelfProfiler::new( + directory, + sopts.crate_name.as_ref().map(|s| &s[..]), + &sopts.debugging_opts.self_profile_events, + ); + match profiler { + Ok(profiler) => Some(Arc::new(profiler)), + Err(e) => { + early_warn(sopts.error_format, &format!("failed to create profiler: {}", e)); + None + } + } + } else { + None + }; + + let parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map); + let sysroot = match &sopts.maybe_sysroot { + Some(sysroot) => sysroot.clone(), + None => filesearch::get_or_default_sysroot(), + }; + + let host_triple = config::host_triple(); + let target_triple = sopts.target_triple.triple(); + let host_tlib_path = SearchPath::from_sysroot_and_triple(&sysroot, host_triple); + let target_tlib_path = if host_triple == target_triple { + None + } else { + Some(SearchPath::from_sysroot_and_triple(&sysroot, target_triple)) + }; + + let file_path_mapping = sopts.file_path_mapping(); + + let local_crate_source_file = + local_crate_source_file.map(|path| file_path_mapping.map_prefix(path).0); + + let optimization_fuel_crate = sopts.debugging_opts.fuel.as_ref().map(|i| i.0.clone()); + let optimization_fuel = Lock::new(OptimizationFuel { + remaining: sopts.debugging_opts.fuel.as_ref().map(|i| i.1).unwrap_or(0), + out_of_fuel: false, + }); + let print_fuel_crate = sopts.debugging_opts.print_fuel.clone(); + let print_fuel = AtomicU64::new(0); + + let working_dir = env::current_dir().unwrap_or_else(|e| { + parse_sess.span_diagnostic.fatal(&format!("Current directory is invalid: {}", e)).raise() + }); + let working_dir = file_path_mapping.map_prefix(working_dir); + + let cgu_reuse_tracker = if sopts.debugging_opts.query_dep_graph { + CguReuseTracker::new() + } else { + CguReuseTracker::new_disabled() + }; + + let prof = SelfProfilerRef::new( + self_profiler, + sopts.debugging_opts.time_passes || sopts.debugging_opts.time, + sopts.debugging_opts.time_passes, + ); + + let ctfe_backtrace = Lock::new(match env::var("RUSTC_CTFE_BACKTRACE") { + Ok(ref val) if val == "immediate" => CtfeBacktrace::Immediate, + Ok(ref val) if val != "0" => CtfeBacktrace::Capture, + _ => CtfeBacktrace::Disabled, + }); + + // Try to find a directory containing the Rust `src`, for more details see + // the doc comment on the `real_rust_source_base_dir` field. + let real_rust_source_base_dir = { + // This is the location used by the `rust-src` `rustup` component. + let mut candidate = sysroot.join("lib/rustlib/src/rust"); + if let Ok(metadata) = candidate.symlink_metadata() { + // Replace the symlink rustbuild creates, with its destination. + // We could try to use `fs::canonicalize` instead, but that might + // produce unnecessarily verbose path. + if metadata.file_type().is_symlink() { + if let Ok(symlink_dest) = std::fs::read_link(&candidate) { + candidate = symlink_dest; + } + } + } + + // Only use this directory if it has a file we can expect to always find. + if candidate.join("library/std/src/lib.rs").is_file() { Some(candidate) } else { None } + }; + + let asm_arch = if target_cfg.target.options.allow_asm { + InlineAsmArch::from_str(&target_cfg.target.arch).ok() + } else { + None + }; + + let sess = Session { + target: target_cfg, + host, + opts: sopts, + host_tlib_path, + target_tlib_path, + parse_sess, + sysroot, + local_crate_source_file, + working_dir, + one_time_diagnostics: Default::default(), + crate_types: OnceCell::new(), + crate_disambiguator: OnceCell::new(), + features: OnceCell::new(), + recursion_limit: OnceCell::new(), + type_length_limit: OnceCell::new(), + const_eval_limit: OnceCell::new(), + incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), + cgu_reuse_tracker, + prof, + perf_stats: PerfStats { + symbol_hash_time: Lock::new(Duration::from_secs(0)), + queries_canonicalized: AtomicUsize::new(0), + normalize_generic_arg_after_erasing_regions: AtomicUsize::new(0), + normalize_projection_ty: AtomicUsize::new(0), + }, + code_stats: Default::default(), + optimization_fuel_crate, + optimization_fuel, + print_fuel_crate, + print_fuel, + jobserver: jobserver::client(), + driver_lint_caps, + trait_methods_not_found: Lock::new(Default::default()), + confused_type_with_std_module: Lock::new(Default::default()), + system_library_path: OneThread::new(RefCell::new(Default::default())), + ctfe_backtrace, + miri_unleashed_features: Lock::new(Default::default()), + real_rust_source_base_dir, + asm_arch, + target_features: FxHashSet::default(), + known_attrs: Lock::new(MarkedAttrs::new()), + used_attrs: Lock::new(MarkedAttrs::new()), + }; + + validate_commandline_args_with_session_available(&sess); + + sess +} + +// If it is useful to have a Session available already for validating a +// commandline argument, you can do so here. +fn validate_commandline_args_with_session_available(sess: &Session) { + // Since we don't know if code in an rlib will be linked to statically or + // dynamically downstream, rustc generates `__imp_` symbols that help linkers + // on Windows deal with this lack of knowledge (#27438). Unfortunately, + // these manually generated symbols confuse LLD when it tries to merge + // bitcode during ThinLTO. Therefore we disallow dynamic linking on Windows + // when compiling for LLD ThinLTO. This way we can validly just not generate + // the `dllimport` attributes and `__imp_` symbols in that case. + if sess.opts.cg.linker_plugin_lto.enabled() + && sess.opts.cg.prefer_dynamic + && sess.target.target.options.is_like_windows + { + sess.err( + "Linker plugin based LTO is not supported together with \ + `-C prefer-dynamic` when targeting Windows-like targets", + ); + } + + // Make sure that any given profiling data actually exists so LLVM can't + // decide to silently skip PGO. + if let Some(ref path) = sess.opts.cg.profile_use { + if !path.exists() { + sess.err(&format!( + "File `{}` passed to `-C profile-use` does not exist.", + path.display() + )); + } + } + + // Unwind tables cannot be disabled if the target requires them. + if let Some(include_uwtables) = sess.opts.cg.force_unwind_tables { + if sess.panic_strategy() == PanicStrategy::Unwind && !include_uwtables { + sess.err( + "panic=unwind requires unwind tables, they cannot be disabled \ + with `-C force-unwind-tables=no`.", + ); + } + + if sess.target.target.options.requires_uwtable && !include_uwtables { + sess.err( + "target requires unwind tables, they cannot be disabled with \ + `-C force-unwind-tables=no`.", + ); + } + } + + // PGO does not work reliably with panic=unwind on Windows. Let's make it + // an error to combine the two for now. It always runs into an assertions + // if LLVM is built with assertions, but without assertions it sometimes + // does not crash and will probably generate a corrupted binary. + // We should only display this error if we're actually going to run PGO. + // If we're just supposed to print out some data, don't show the error (#61002). + if sess.opts.cg.profile_generate.enabled() + && sess.target.target.options.is_like_msvc + && sess.panic_strategy() == PanicStrategy::Unwind + && sess.opts.prints.iter().all(|&p| p == PrintRequest::NativeStaticLibs) + { + sess.err( + "Profile-guided optimization does not yet work in conjunction \ + with `-Cpanic=unwind` on Windows when targeting MSVC. \ + See issue #61002 <https://github.com/rust-lang/rust/issues/61002> \ + for more information.", + ); + } + + // FIXME(richkadel): See `src/test/run-make-fulldeps/instrument-coverage/Makefile`. After + // compiling with `-Zinstrument-coverage`, the resulting binary generates a segfault during + // the program's exit process (likely while attempting to generate the coverage stats in + // the "*.profraw" file). An investigation to resolve the problem on Windows is ongoing, + // but until this is resolved, the option is disabled on Windows, and the test is skipped + // when targeting `MSVC`. + if sess.opts.debugging_opts.instrument_coverage && sess.target.target.options.is_like_msvc { + sess.warn( + "Rust source-based code coverage instrumentation (with `-Z instrument-coverage`) \ + is not yet supported on Windows when targeting MSVC. The resulting binaries will \ + still be instrumented for experimentation purposes, but may not execute correctly.", + ); + } + + const ASAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-fuchsia", + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-fuchsia", + "x86_64-unknown-freebsd", + "x86_64-unknown-linux-gnu", + ]; + const LSAN_SUPPORTED_TARGETS: &[&str] = + &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + const MSAN_SUPPORTED_TARGETS: &[&str] = + &["aarch64-unknown-linux-gnu", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu"]; + const TSAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-unknown-freebsd", + "x86_64-unknown-linux-gnu", + ]; + + // Sanitizers can only be used on some tested platforms. + for s in sess.opts.debugging_opts.sanitizer { + let supported_targets = match s { + SanitizerSet::ADDRESS => ASAN_SUPPORTED_TARGETS, + SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS, + SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS, + SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS, + _ => panic!("unrecognized sanitizer {}", s), + }; + if !supported_targets.contains(&&*sess.opts.target_triple.triple()) { + sess.err(&format!( + "`-Zsanitizer={}` only works with targets: {}", + s, + supported_targets.join(", ") + )); + } + let conflicting = sess.opts.debugging_opts.sanitizer - s; + if !conflicting.is_empty() { + sess.err(&format!( + "`-Zsanitizer={}` is incompatible with `-Zsanitizer={}`", + s, conflicting, + )); + // Don't report additional errors. + break; + } + } +} + +/// Holds data on the current incremental compilation session, if there is one. +#[derive(Debug)] +pub enum IncrCompSession { + /// This is the state the session will be in until the incr. comp. dir is + /// needed. + NotInitialized, + /// This is the state during which the session directory is private and can + /// be modified. + Active { session_directory: PathBuf, lock_file: flock::Lock, load_dep_graph: bool }, + /// This is the state after the session directory has been finalized. In this + /// state, the contents of the directory must not be modified any more. + Finalized { session_directory: PathBuf }, + /// This is an error state that is reached when some compilation error has + /// occurred. It indicates that the contents of the session directory must + /// not be used, since they might be invalid. + InvalidBecauseOfErrors { session_directory: PathBuf }, +} + +pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { + let emitter: Box<dyn Emitter + sync::Send> = match output { + config::ErrorOutputType::HumanReadable(kind) => { + let (short, color_config) = kind.unzip(); + Box::new(EmitterWriter::stderr(color_config, None, short, false, None, false)) + } + config::ErrorOutputType::Json { pretty, json_rendered } => { + Box::new(JsonEmitter::basic(pretty, json_rendered, None, false)) + } + }; + let handler = rustc_errors::Handler::with_emitter(true, None, emitter); + handler.struct_fatal(msg).emit(); + rustc_errors::FatalError.raise(); +} + +pub fn early_warn(output: config::ErrorOutputType, msg: &str) { + let emitter: Box<dyn Emitter + sync::Send> = match output { + config::ErrorOutputType::HumanReadable(kind) => { + let (short, color_config) = kind.unzip(); + Box::new(EmitterWriter::stderr(color_config, None, short, false, None, false)) + } + config::ErrorOutputType::Json { pretty, json_rendered } => { + Box::new(JsonEmitter::basic(pretty, json_rendered, None, false)) + } + }; + let handler = rustc_errors::Handler::with_emitter(true, None, emitter); + handler.struct_warn(msg).emit(); +} + +pub type CompileResult = Result<(), ErrorReported>; diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs new file mode 100644 index 00000000000..15447c01d1e --- /dev/null +++ b/compiler/rustc_session/src/utils.rs @@ -0,0 +1,32 @@ +use crate::session::Session; +use rustc_data_structures::profiling::VerboseTimingGuard; + +impl Session { + pub fn timer<'a>(&'a self, what: &'static str) -> VerboseTimingGuard<'a> { + self.prof.verbose_generic_activity(what) + } + pub fn time<R>(&self, what: &'static str, f: impl FnOnce() -> R) -> R { + self.prof.verbose_generic_activity(what).run(f) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] +pub enum NativeLibKind { + /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) included + /// when linking a final binary, but not when archiving an rlib. + StaticNoBundle, + /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) included + /// when linking a final binary, but also included when archiving an rlib. + StaticBundle, + /// Dynamic library (e.g. `libfoo.so` on Linux) + /// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC). + Dylib, + /// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library. + RawDylib, + /// A macOS-specific kind of dynamic libraries. + Framework, + /// The library kind wasn't specified, `Dylib` is currently used as a default. + Unspecified, +} + +rustc_data_structures::impl_stable_hash_via_hash!(NativeLibKind); |
