pub use crate::passes::BoxedResolver; use crate::util; use rustc::ty; use rustc::util::common::ErrorReported; use rustc_ast::ast::{self, MetaItemKind}; use rustc_ast::token; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::OnDrop; use rustc_errors::registry::Registry; use rustc_lint::LintStore; use rustc_parse::new_parser_from_source_str; use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames}; use rustc_session::early_error; use rustc_session::lint; use rustc_session::parse::{CrateConfig, ParseSess}; use rustc_session::{DiagnosticOutput, Session}; use rustc_span::edition; use rustc_span::source_map::{FileLoader, FileName, SourceMap}; use std::path::PathBuf; use std::result; use std::sync::{Arc, Mutex}; pub type Result = result::Result; /// Represents a compiler session. /// Can be used to run `rustc_interface` queries. /// Created by passing `Config` to `run_compiler`. pub struct Compiler { pub(crate) sess: Lrc, codegen_backend: Lrc>, source_map: Lrc, pub(crate) input: Input, pub(crate) input_path: Option, pub(crate) output_dir: Option, pub(crate) output_file: Option, pub(crate) crate_name: Option, pub(crate) register_lints: Option>, pub(crate) override_queries: Option, &mut ty::query::Providers<'_>)>, } impl Compiler { pub fn session(&self) -> &Lrc { &self.sess } pub fn codegen_backend(&self) -> &Lrc> { &self.codegen_backend } pub fn source_map(&self) -> &Lrc { &self.source_map } pub fn input(&self) -> &Input { &self.input } pub fn output_dir(&self) -> &Option { &self.output_dir } pub fn output_file(&self) -> &Option { &self.output_file } pub fn build_output_filenames( &self, sess: &Session, attrs: &[ast::Attribute], ) -> OutputFilenames { util::build_output_filenames( &self.input, &self.output_dir, &self.output_file, &attrs, &sess, ) } } /// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`. pub fn parse_cfgspecs(cfgspecs: Vec) -> FxHashSet<(String, Option)> { rustc_ast::with_default_globals(move || { let cfg = cfgspecs .into_iter() .map(|s| { let sess = ParseSess::with_silent_emitter(); let filename = FileName::cfg_spec_source_code(&s); let mut parser = new_parser_from_source_str(&sess, filename, s.to_string()); macro_rules! error { ($reason: expr) => { early_error( ErrorOutputType::default(), &format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s), ); }; } match &mut parser.parse_meta_item() { Ok(meta_item) if parser.token == token::Eof => { if meta_item.path.segments.len() != 1 { error!("argument key must be an identifier"); } match &meta_item.kind { MetaItemKind::List(..) => { error!(r#"expected `key` or `key="value"`"#); } MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { error!("argument value must be a string"); } MetaItemKind::NameValue(..) | MetaItemKind::Word => { let ident = meta_item.ident().expect("multi-segment cfg key"); return (ident.name, meta_item.value_str()); } } } Ok(..) => {} Err(err) => err.cancel(), } error!(r#"expected `key` or `key="value"`"#); }) .collect::(); cfg.into_iter().map(|(a, b)| (a.to_string(), b.map(|b| b.to_string()))).collect() }) } /// The compiler configuration pub struct Config { /// Command line options pub opts: config::Options, /// cfg! configuration in addition to the default ones pub crate_cfg: FxHashSet<(String, Option)>, pub input: Input, pub input_path: Option, pub output_dir: Option, pub output_file: Option, pub file_loader: Option>, pub diagnostic_output: DiagnosticOutput, /// Set to capture stderr output during compiler execution pub stderr: Option>>>, pub crate_name: Option, pub lint_caps: FxHashMap, /// This is a callback from the driver that is called when we're registering lints; /// it is called during plugin registration when we have the LintStore in a non-shared state. /// /// Note that if you find a Some here you probably want to call that function in the new /// function being registered. pub register_lints: Option>, /// This is a callback from the driver that is called just after we have populated /// the list of queries. /// /// The second parameter is local providers and the third parameter is external providers. pub override_queries: Option, &mut ty::query::Providers<'_>)>, /// Registry of diagnostics codes. pub registry: Registry, } pub fn run_compiler_in_existing_thread_pool( config: Config, f: impl FnOnce(&Compiler) -> R, ) -> R { let registry = &config.registry; let (sess, codegen_backend, source_map) = util::create_session( config.opts, config.crate_cfg, config.diagnostic_output, config.file_loader, config.input_path.clone(), config.lint_caps, registry.clone(), ); let compiler = Compiler { sess, codegen_backend, source_map, input: config.input, input_path: config.input_path, output_dir: config.output_dir, output_file: config.output_file, crate_name: config.crate_name, register_lints: config.register_lints, override_queries: config.override_queries, }; let r = { let _sess_abort_error = OnDrop(|| { compiler.sess.diagnostic().print_error_count(registry); }); f(&compiler) }; let prof = compiler.sess.prof.clone(); prof.generic_activity("drop_compiler").run(move || drop(compiler)); r } pub fn run_compiler(mut config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { let stderr = config.stderr.take(); util::spawn_thread_pool( config.opts.edition, config.opts.debugging_opts.threads, &stderr, || run_compiler_in_existing_thread_pool(config, f), ) } pub fn default_thread_pool(edition: edition::Edition, f: impl FnOnce() -> R + Send) -> R { // the 1 here is duplicating code in config.opts.debugging_opts.threads // which also defaults to 1; it ultimately doesn't matter as the default // isn't threaded, and just ignores this parameter util::spawn_thread_pool(edition, 1, &None, f) }