1 pub use crate::passes
::BoxedResolver
;
5 use rustc_ast
::{self as ast, LitKind, MetaItemKind}
;
6 use rustc_codegen_ssa
::traits
::CodegenBackend
;
7 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
8 use rustc_data_structures
::sync
::Lrc
;
9 use rustc_data_structures
::OnDrop
;
10 use rustc_errors
::registry
::Registry
;
11 use rustc_errors
::{ErrorGuaranteed, Handler}
;
12 use rustc_lint
::LintStore
;
14 use rustc_parse
::maybe_new_parser_from_source_str
;
15 use rustc_query_impl
::QueryCtxt
;
16 use rustc_session
::config
::{self, CheckCfg, ErrorOutputType, Input, OutputFilenames}
;
17 use rustc_session
::early_error
;
18 use rustc_session
::lint
;
19 use rustc_session
::parse
::{CrateConfig, ParseSess}
;
20 use rustc_session
::Session
;
21 use rustc_span
::source_map
::{FileLoader, FileName}
;
22 use rustc_span
::symbol
::sym
;
23 use std
::path
::PathBuf
;
26 pub type Result
<T
> = result
::Result
<T
, ErrorGuaranteed
>;
28 /// Represents a compiler session. Note that every `Compiler` contains a
29 /// `Session`, but `Compiler` also contains some things that cannot be in
30 /// `Session`, due to `Session` being in a crate that has many fewer
31 /// dependencies than this crate.
33 /// Can be used to run `rustc_interface` queries.
34 /// Created by passing [`Config`] to [`run_compiler`].
36 pub(crate) sess
: Lrc
<Session
>,
37 codegen_backend
: Lrc
<Box
<dyn CodegenBackend
>>,
38 pub(crate) input
: Input
,
39 pub(crate) input_path
: Option
<PathBuf
>,
40 pub(crate) output_dir
: Option
<PathBuf
>,
41 pub(crate) output_file
: Option
<PathBuf
>,
42 pub(crate) temps_dir
: Option
<PathBuf
>,
43 pub(crate) register_lints
: Option
<Box
<dyn Fn(&Session
, &mut LintStore
) + Send
+ Sync
>>,
44 pub(crate) override_queries
:
45 Option
<fn(&Session
, &mut ty
::query
::Providers
, &mut ty
::query
::ExternProviders
)>,
49 pub fn session(&self) -> &Lrc
<Session
> {
52 pub fn codegen_backend(&self) -> &Lrc
<Box
<dyn CodegenBackend
>> {
55 pub fn input(&self) -> &Input
{
58 pub fn output_dir(&self) -> &Option
<PathBuf
> {
61 pub fn output_file(&self) -> &Option
<PathBuf
> {
64 pub fn temps_dir(&self) -> &Option
<PathBuf
> {
67 pub fn register_lints(&self) -> &Option
<Box
<dyn Fn(&Session
, &mut LintStore
) + Send
+ Sync
>> {
70 pub fn build_output_filenames(
73 attrs
: &[ast
::Attribute
],
74 ) -> OutputFilenames
{
75 util
::build_output_filenames(
86 /// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`.
87 pub fn parse_cfgspecs(cfgspecs
: Vec
<String
>) -> FxHashSet
<(String
, Option
<String
>)> {
88 rustc_span
::create_default_session_if_not_set_then(move |_
| {
92 let sess
= ParseSess
::with_silent_emitter(Some(format
!(
93 "this error occurred on the command line: `--cfg={}`",
96 let filename
= FileName
::cfg_spec_source_code(&s
);
101 ErrorOutputType
::default(),
102 &format
!(concat
!("invalid `--cfg` argument: `{}` (", $reason
, ")"), s
),
107 match maybe_new_parser_from_source_str(&sess
, filename
, s
.to_string()) {
108 Ok(mut parser
) => match parser
.parse_meta_item() {
109 Ok(meta_item
) if parser
.token
== token
::Eof
=> {
110 if meta_item
.path
.segments
.len() != 1 {
111 error
!("argument key must be an identifier");
113 match &meta_item
.kind
{
114 MetaItemKind
::List(..) => {}
115 MetaItemKind
::NameValue(lit
) if !lit
.kind
.is_str() => {
116 error
!("argument value must be a string");
118 MetaItemKind
::NameValue(..) | MetaItemKind
::Word
=> {
119 let ident
= meta_item
.ident().expect("multi-segment cfg key");
120 return (ident
.name
, meta_item
.value_str());
125 Err(err
) => err
.cancel(),
127 Err(errs
) => drop(errs
),
130 // If the user tried to use a key="value" flag, but is missing the quotes, provide
131 // a hint about how to resolve this.
132 if s
.contains('
='
) && !s
.contains("=\"") && !s
.ends_with('
"') {
134 r#"expected `key` or `key
="value"`
, ensure escaping is appropriate
"#,
135 r#" for your shell
, try 'key
="value"' or key
=\"value
\""#
138 error!(r#"expected `key` or `key
="value"`
"#);
141 .collect::<CrateConfig>();
142 cfg.into_iter().map(|(a, b)| (a.to_string(), b.map(|b| b.to_string()))).collect()
146 /// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
147 pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
148 rustc_span::create_default_session_if_not_set_then(move |_| {
149 let mut cfg = CheckCfg::default();
151 'specs: for s in specs {
152 let sess = ParseSess::with_silent_emitter(Some(format!(
153 "this error occurred on the command line
: `
--check
-cfg
={}`
",
156 let filename = FileName::cfg_spec_source_code(&s);
161 ErrorOutputType::default(),
163 concat!("invalid `
--check
-cfg` argument
: `{}`
(", $reason, ")"),
170 match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
171 Ok(mut parser) => match parser.parse_meta_item() {
172 Ok(meta_item) if parser.token == token::Eof => {
173 if let Some(args) = meta_item.meta_item_list() {
174 if meta_item.has_name(sym::names) {
176 cfg.names_valid.get_or_insert_with(|| FxHashSet::default());
178 if arg.is_word() && arg.ident().is_some() {
179 let ident = arg.ident().expect("multi
-segment cfg key
");
180 names_valid.insert(ident.name.to_string());
182 error!("`
names()` arguments must be simple identifiers
");
186 } else if meta_item.has_name(sym::values) {
187 if let Some((name, values)) = args.split_first() {
188 if name.is_word() && name.ident().is_some() {
189 let ident = name.ident().expect("multi
-segment cfg key
");
190 let ident_values = cfg
192 .entry(ident.name.to_string())
193 .or_insert_with(|| FxHashSet::default());
196 if let Some(LitKind::Str(s, _)) =
197 val.literal().map(|lit| &lit.kind)
199 ident_values.insert(s.to_string());
202 "`
values()` arguments must be string literals
"
210 "`
values()` first argument must be a simple identifier
"
213 } else if args.is_empty() {
214 cfg.well_known_values = true;
221 Err(err) => err.cancel(),
223 Err(errs) => drop(errs),
227 "expected `
names(name1
, name2
, ... nameN
)` or
\
228 `
values(name
, \"value1
\", \"value2
\", ... \"valueN
\")`
"
232 if let Some(names_valid) = &mut cfg.names_valid {
233 names_valid.extend(cfg.values_valid.keys().cloned());
239 /// The compiler configuration
241 /// Command line options
242 pub opts: config::Options,
244 /// cfg! configuration in addition to the default ones
245 pub crate_cfg: FxHashSet<(String, Option<String>)>,
246 pub crate_check_cfg: CheckCfg,
249 pub input_path: Option<PathBuf>,
250 pub output_dir: Option<PathBuf>,
251 pub output_file: Option<PathBuf>,
252 pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
254 pub lint_caps: FxHashMap<lint::LintId, lint::Level>,
256 /// This is a callback from the driver that is called when [`ParseSess`] is created.
257 pub parse_sess_created: Option<Box<dyn FnOnce(&mut ParseSess) + Send>>,
259 /// This is a callback from the driver that is called when we're registering lints;
260 /// it is called during plugin registration when we have the LintStore in a non-shared state.
262 /// Note that if you find a Some here you probably want to call that function in the new
263 /// function being registered.
264 pub register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
266 /// This is a callback from the driver that is called just after we have populated
267 /// the list of queries.
269 /// The second parameter is local providers and the third parameter is external providers.
270 pub override_queries:
271 Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::ExternProviders)>,
273 /// This is a callback from the driver that is called to create a codegen backend.
274 pub make_codegen_backend:
275 Option<Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>>,
277 /// Registry of diagnostics codes.
278 pub registry: Registry,
281 // JUSTIFICATION: before session exists, only config
282 #[allow(rustc::bad_opt_access)]
283 pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
284 trace!("run_compiler
");
285 util::run_in_thread_pool_with_globals(
287 config.opts.unstable_opts.threads,
289 crate::callbacks::setup_callbacks();
291 let registry = &config.registry;
292 let (mut sess, codegen_backend) = util::create_session(
295 config.crate_check_cfg,
297 config.input_path.clone(),
299 config.make_codegen_backend,
303 if let Some(parse_sess_created) = config.parse_sess_created {
304 parse_sess_created(&mut sess.parse_sess);
307 let temps_dir = sess.opts.unstable_opts.temps_dir.as_ref().map(|o| PathBuf::from(&o));
309 let compiler = Compiler {
310 sess: Lrc::new(sess),
311 codegen_backend: Lrc::new(codegen_backend),
313 input_path: config.input_path,
314 output_dir: config.output_dir,
315 output_file: config.output_file,
317 register_lints: config.register_lints,
318 override_queries: config.override_queries,
321 rustc_span::with_source_map(compiler.sess.parse_sess.clone_source_map(), move || {
323 let _sess_abort_error = OnDrop(|| {
324 compiler.sess.finish_diagnostics(registry);
330 let prof = compiler.sess.prof.clone();
331 prof.generic_activity("drop_compiler
").run(move || drop(compiler));
338 pub fn try_print_query_stack(handler: &Handler, num_frames: Option<usize>) {
339 eprintln!("query stack during panic
:");
341 // Be careful relying on global state here: this code is called from
342 // a panic hook, which means that the global `Handler` may be in a weird
343 // state if it was responsible for triggering the panic.
344 let i = ty::tls::with_context_opt(|icx| {
345 if let Some(icx) = icx {
346 QueryCtxt::from_tcx(icx.tcx).try_print_query_stack(icx.query, handler, num_frames)
352 if num_frames == None || num_frames >= Some(i) {
353 eprintln!("end of query stack
");
355 eprintln!("we're just showing a limited slice of the query stack
");