]>
Commit | Line | Data |
---|---|---|
532ac7d7 | 1 | pub use crate::passes::BoxedResolver; |
dfeec247 | 2 | use crate::util; |
532ac7d7 | 3 | |
74b04a01 | 4 | use rustc_ast::token; |
5099ac24 | 5 | use rustc_ast::{self as ast, LitKind, MetaItemKind}; |
ba9703b0 | 6 | use rustc_codegen_ssa::traits::CodegenBackend; |
dfeec247 | 7 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
532ac7d7 | 8 | use rustc_data_structures::sync::Lrc; |
dfeec247 | 9 | use rustc_data_structures::OnDrop; |
60c5eb7d | 10 | use rustc_errors::registry::Registry; |
5e7ed085 | 11 | use rustc_errors::{ErrorGuaranteed, Handler}; |
dfeec247 | 12 | use rustc_lint::LintStore; |
ba9703b0 | 13 | use rustc_middle::ty; |
a2a8927a | 14 | use rustc_parse::maybe_new_parser_from_source_str; |
136023e0 | 15 | use rustc_query_impl::QueryCtxt; |
5099ac24 | 16 | use rustc_session::config::{self, CheckCfg, ErrorOutputType, Input, OutputFilenames}; |
ba9703b0 XL |
17 | use rustc_session::early_error; |
18 | use rustc_session::lint; | |
74b04a01 | 19 | use rustc_session::parse::{CrateConfig, ParseSess}; |
2b03887a | 20 | use rustc_session::Session; |
f9f354fc | 21 | use rustc_span::source_map::{FileLoader, FileName}; |
5099ac24 | 22 | use rustc_span::symbol::sym; |
532ac7d7 XL |
23 | use std::path::PathBuf; |
24 | use std::result; | |
532ac7d7 | 25 | |
5e7ed085 | 26 | pub type Result<T> = result::Result<T, ErrorGuaranteed>; |
532ac7d7 | 27 | |
2b03887a FG |
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. | |
fc512014 | 32 | /// |
dfeec247 | 33 | /// Can be used to run `rustc_interface` queries. |
fc512014 | 34 | /// Created by passing [`Config`] to [`run_compiler`]. |
532ac7d7 XL |
35 | pub struct Compiler { |
36 | pub(crate) sess: Lrc<Session>, | |
37 | codegen_backend: Lrc<Box<dyn CodegenBackend>>, | |
532ac7d7 XL |
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>, | |
3c0e092e | 42 | pub(crate) temps_dir: Option<PathBuf>, |
dfeec247 | 43 | pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>, |
60c5eb7d | 44 | pub(crate) override_queries: |
3c0e092e | 45 | Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::ExternProviders)>, |
532ac7d7 XL |
46 | } |
47 | ||
48 | impl Compiler { | |
49 | pub fn session(&self) -> &Lrc<Session> { | |
50 | &self.sess | |
51 | } | |
52 | pub fn codegen_backend(&self) -> &Lrc<Box<dyn CodegenBackend>> { | |
53 | &self.codegen_backend | |
54 | } | |
532ac7d7 XL |
55 | pub fn input(&self) -> &Input { |
56 | &self.input | |
57 | } | |
58 | pub fn output_dir(&self) -> &Option<PathBuf> { | |
59 | &self.output_dir | |
60 | } | |
61 | pub fn output_file(&self) -> &Option<PathBuf> { | |
62 | &self.output_file | |
63 | } | |
3c0e092e XL |
64 | pub fn temps_dir(&self) -> &Option<PathBuf> { |
65 | &self.temps_dir | |
66 | } | |
fc512014 XL |
67 | pub fn register_lints(&self) -> &Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>> { |
68 | &self.register_lints | |
69 | } | |
74b04a01 XL |
70 | pub fn build_output_filenames( |
71 | &self, | |
72 | sess: &Session, | |
73 | attrs: &[ast::Attribute], | |
74 | ) -> OutputFilenames { | |
3c0e092e XL |
75 | util::build_output_filenames( |
76 | &self.input, | |
77 | &self.output_dir, | |
78 | &self.output_file, | |
79 | &self.temps_dir, | |
80 | attrs, | |
81 | sess, | |
82 | ) | |
74b04a01 | 83 | } |
532ac7d7 XL |
84 | } |
85 | ||
e74abb32 XL |
86 | /// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`. |
87 | pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> { | |
136023e0 | 88 | rustc_span::create_default_session_if_not_set_then(move |_| { |
dfeec247 XL |
89 | let cfg = cfgspecs |
90 | .into_iter() | |
91 | .map(|s| { | |
3c0e092e XL |
92 | let sess = ParseSess::with_silent_emitter(Some(format!( |
93 | "this error occurred on the command line: `--cfg={}`", | |
94 | s | |
95 | ))); | |
dfeec247 | 96 | let filename = FileName::cfg_spec_source_code(&s); |
dfeec247 XL |
97 | |
98 | macro_rules! error { | |
99 | ($reason: expr) => { | |
100 | early_error( | |
101 | ErrorOutputType::default(), | |
102 | &format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s), | |
103 | ); | |
104 | }; | |
105 | } | |
106 | ||
a2a8927a | 107 | match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) { |
5e7ed085 | 108 | Ok(mut parser) => match parser.parse_meta_item() { |
a2a8927a XL |
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"); | |
dfeec247 | 112 | } |
a2a8927a XL |
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"); | |
117 | } | |
118 | MetaItemKind::NameValue(..) | MetaItemKind::Word => { | |
119 | let ident = meta_item.ident().expect("multi-segment cfg key"); | |
120 | return (ident.name, meta_item.value_str()); | |
121 | } | |
dfeec247 | 122 | } |
e74abb32 | 123 | } |
a2a8927a XL |
124 | Ok(..) => {} |
125 | Err(err) => err.cancel(), | |
126 | }, | |
5e7ed085 | 127 | Err(errs) => drop(errs), |
e74abb32 | 128 | } |
dfeec247 | 129 | |
5099ac24 FG |
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('"') { | |
133 | error!(concat!( | |
134 | r#"expected `key` or `key="value"`, ensure escaping is appropriate"#, | |
135 | r#" for your shell, try 'key="value"' or key=\"value\""# | |
136 | )); | |
137 | } else { | |
138 | error!(r#"expected `key` or `key="value"`"#); | |
139 | } | |
dfeec247 | 140 | }) |
74b04a01 | 141 | .collect::<CrateConfig>(); |
dfeec247 | 142 | cfg.into_iter().map(|(a, b)| (a.to_string(), b.map(|b| b.to_string()))).collect() |
e74abb32 XL |
143 | }) |
144 | } | |
145 | ||
5099ac24 FG |
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(); | |
150 | ||
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={}`", | |
154 | s | |
155 | ))); | |
156 | let filename = FileName::cfg_spec_source_code(&s); | |
157 | ||
158 | macro_rules! error { | |
159 | ($reason: expr) => { | |
160 | early_error( | |
161 | ErrorOutputType::default(), | |
162 | &format!( | |
163 | concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"), | |
164 | s | |
165 | ), | |
166 | ); | |
167 | }; | |
168 | } | |
169 | ||
170 | match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) { | |
5e7ed085 | 171 | Ok(mut parser) => match parser.parse_meta_item() { |
5099ac24 FG |
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) { | |
5e7ed085 FG |
175 | let names_valid = |
176 | cfg.names_valid.get_or_insert_with(|| FxHashSet::default()); | |
5099ac24 FG |
177 | for arg in args { |
178 | if arg.is_word() && arg.ident().is_some() { | |
179 | let ident = arg.ident().expect("multi-segment cfg key"); | |
5e7ed085 | 180 | names_valid.insert(ident.name.to_string()); |
5099ac24 | 181 | } else { |
f2b60f7d | 182 | error!("`names()` arguments must be simple identifiers"); |
5099ac24 FG |
183 | } |
184 | } | |
185 | continue 'specs; | |
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"); | |
5e7ed085 FG |
190 | let ident_values = cfg |
191 | .values_valid | |
192 | .entry(ident.name.to_string()) | |
193 | .or_insert_with(|| FxHashSet::default()); | |
194 | ||
5099ac24 FG |
195 | for val in values { |
196 | if let Some(LitKind::Str(s, _)) = | |
487cf647 | 197 | val.lit().map(|lit| &lit.kind) |
5099ac24 | 198 | { |
5e7ed085 | 199 | ident_values.insert(s.to_string()); |
5099ac24 FG |
200 | } else { |
201 | error!( | |
202 | "`values()` arguments must be string literals" | |
203 | ); | |
204 | } | |
205 | } | |
206 | ||
207 | continue 'specs; | |
208 | } else { | |
209 | error!( | |
f2b60f7d | 210 | "`values()` first argument must be a simple identifier" |
5099ac24 FG |
211 | ); |
212 | } | |
5e7ed085 FG |
213 | } else if args.is_empty() { |
214 | cfg.well_known_values = true; | |
215 | continue 'specs; | |
5099ac24 FG |
216 | } |
217 | } | |
218 | } | |
219 | } | |
220 | Ok(..) => {} | |
221 | Err(err) => err.cancel(), | |
222 | }, | |
5e7ed085 | 223 | Err(errs) => drop(errs), |
5099ac24 FG |
224 | } |
225 | ||
226 | error!( | |
227 | "expected `names(name1, name2, ... nameN)` or \ | |
228 | `values(name, \"value1\", \"value2\", ... \"valueN\")`" | |
229 | ); | |
230 | } | |
231 | ||
5e7ed085 FG |
232 | if let Some(names_valid) = &mut cfg.names_valid { |
233 | names_valid.extend(cfg.values_valid.keys().cloned()); | |
234 | } | |
5099ac24 FG |
235 | cfg |
236 | }) | |
237 | } | |
238 | ||
532ac7d7 XL |
239 | /// The compiler configuration |
240 | pub struct Config { | |
241 | /// Command line options | |
242 | pub opts: config::Options, | |
243 | ||
244 | /// cfg! configuration in addition to the default ones | |
245 | pub crate_cfg: FxHashSet<(String, Option<String>)>, | |
5099ac24 | 246 | pub crate_check_cfg: CheckCfg, |
532ac7d7 XL |
247 | |
248 | pub input: Input, | |
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>>, | |
532ac7d7 | 253 | |
532ac7d7 | 254 | pub lint_caps: FxHashMap<lint::LintId, lint::Level>, |
e74abb32 | 255 | |
6a06907d XL |
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>>, | |
258 | ||
e74abb32 XL |
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. | |
261 | /// | |
262 | /// Note that if you find a Some here you probably want to call that function in the new | |
263 | /// function being registered. | |
dfeec247 | 264 | pub register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>, |
60c5eb7d XL |
265 | |
266 | /// This is a callback from the driver that is called just after we have populated | |
267 | /// the list of queries. | |
268 | /// | |
269 | /// The second parameter is local providers and the third parameter is external providers. | |
270 | pub override_queries: | |
3c0e092e | 271 | Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::ExternProviders)>, |
60c5eb7d | 272 | |
1b1a35ee XL |
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>>, | |
276 | ||
60c5eb7d XL |
277 | /// Registry of diagnostics codes. |
278 | pub registry: Registry, | |
532ac7d7 XL |
279 | } |
280 | ||
064997fb | 281 | // JUSTIFICATION: before session exists, only config |
f2b60f7d | 282 | #[allow(rustc::bad_opt_access)] |
5099ac24 | 283 | pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { |
f2b60f7d | 284 | trace!("run_compiler"); |
5099ac24 | 285 | util::run_in_thread_pool_with_globals( |
dc9dc135 | 286 | config.opts.edition, |
064997fb | 287 | config.opts.unstable_opts.threads, |
2b03887a FG |
288 | || { |
289 | crate::callbacks::setup_callbacks(); | |
290 | ||
291 | let registry = &config.registry; | |
292 | let (mut sess, codegen_backend) = util::create_session( | |
293 | config.opts, | |
294 | config.crate_cfg, | |
295 | config.crate_check_cfg, | |
296 | config.file_loader, | |
297 | config.input_path.clone(), | |
298 | config.lint_caps, | |
299 | config.make_codegen_backend, | |
300 | registry.clone(), | |
301 | ); | |
302 | ||
303 | if let Some(parse_sess_created) = config.parse_sess_created { | |
304 | parse_sess_created(&mut sess.parse_sess); | |
305 | } | |
306 | ||
487cf647 | 307 | let temps_dir = sess.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); |
2b03887a FG |
308 | |
309 | let compiler = Compiler { | |
310 | sess: Lrc::new(sess), | |
311 | codegen_backend: Lrc::new(codegen_backend), | |
312 | input: config.input, | |
313 | input_path: config.input_path, | |
314 | output_dir: config.output_dir, | |
315 | output_file: config.output_file, | |
316 | temps_dir, | |
317 | register_lints: config.register_lints, | |
318 | override_queries: config.override_queries, | |
319 | }; | |
320 | ||
321 | rustc_span::with_source_map(compiler.sess.parse_sess.clone_source_map(), move || { | |
322 | let r = { | |
323 | let _sess_abort_error = OnDrop(|| { | |
324 | compiler.sess.finish_diagnostics(registry); | |
325 | }); | |
326 | ||
327 | f(&compiler) | |
328 | }; | |
329 | ||
330 | let prof = compiler.sess.prof.clone(); | |
331 | prof.generic_activity("drop_compiler").run(move || drop(compiler)); | |
332 | r | |
333 | }) | |
334 | }, | |
532ac7d7 XL |
335 | ) |
336 | } | |
6a06907d XL |
337 | |
338 | pub fn try_print_query_stack(handler: &Handler, num_frames: Option<usize>) { | |
339 | eprintln!("query stack during panic:"); | |
340 | ||
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 { | |
136023e0 | 346 | QueryCtxt::from_tcx(icx.tcx).try_print_query_stack(icx.query, handler, num_frames) |
6a06907d XL |
347 | } else { |
348 | 0 | |
349 | } | |
350 | }); | |
351 | ||
352 | if num_frames == None || num_frames >= Some(i) { | |
353 | eprintln!("end of query stack"); | |
354 | } else { | |
355 | eprintln!("we're just showing a limited slice of the query stack"); | |
356 | } | |
357 | } |