5 //! This API is completely unstable and subject to change.
7 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
8 #![feature(is_terminal)]
10 #![feature(decl_macro)]
11 #![recursion_limit = "256"]
12 #![allow(rustc::potential_query_instability)]
13 #![deny(rustc::untranslatable_diagnostic)]
14 #![deny(rustc::diagnostic_outside_of_impl)]
19 pub extern crate rustc_plugin_impl
as plugin
;
22 use rustc_codegen_ssa
::{traits::CodegenBackend, CodegenErrors, CodegenResults}
;
23 use rustc_data_structures
::profiling
::{get_resident_set_size, print_time_passes_entry}
;
24 use rustc_data_structures
::sync
::SeqCst
;
25 use rustc_errors
::registry
::{InvalidErrorCode, Registry}
;
26 use rustc_errors
::{ErrorGuaranteed, PResult}
;
27 use rustc_feature
::find_gated_cfg
;
28 use rustc_hir
::def_id
::LOCAL_CRATE
;
29 use rustc_interface
::util
::{self, collect_crate_types, get_codegen_backend}
;
30 use rustc_interface
::{interface, Queries}
;
31 use rustc_lint
::LintStore
;
32 use rustc_metadata
::locator
;
33 use rustc_save_analysis
as save
;
34 use rustc_save_analysis
::DumpHandler
;
35 use rustc_session
::config
::{nightly_options, CG_OPTIONS, Z_OPTIONS}
;
36 use rustc_session
::config
::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths}
;
37 use rustc_session
::cstore
::MetadataLoader
;
38 use rustc_session
::getopts
;
39 use rustc_session
::lint
::{Lint, LintId}
;
40 use rustc_session
::{config, Session}
;
41 use rustc_session
::{early_error, early_error_no_abort, early_warn}
;
42 use rustc_span
::source_map
::{FileLoader, FileName}
;
43 use rustc_span
::symbol
::sym
;
44 use rustc_target
::json
::ToJson
;
49 use std
::ffi
::OsString
;
51 use std
::io
::{self, IsTerminal, Read, Write}
;
52 use std
::panic
::{self, catch_unwind}
;
53 use std
::path
::PathBuf
;
54 use std
::process
::{self, Command, Stdio}
;
56 use std
::sync
::LazyLock
;
57 use std
::time
::Instant
;
61 mod session_diagnostics
;
63 use crate::session_diagnostics
::{
64 RLinkEmptyVersionNumber
, RLinkEncodingVersionMismatch
, RLinkRustcVersionMismatch
,
65 RLinkWrongFileType
, RlinkNotAFile
, RlinkUnableToRead
,
68 /// Exit status code used for successful compilation and help output.
69 pub const EXIT_SUCCESS
: i32 = 0;
71 /// Exit status code used for compilation failures and invalid flags.
72 pub const EXIT_FAILURE
: i32 = 1;
74 const BUG_REPORT_URL
: &str = "https://github.com/rust-lang/rust/issues/new\
75 ?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
77 const ICE_REPORT_COMPILER_FLAGS
: &[&str] = &["-Z", "-C", "--crate-type"];
79 const ICE_REPORT_COMPILER_FLAGS_EXCLUDE
: &[&str] = &["metadata", "extra-filename"];
81 const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE
: &[&str] = &["incremental"];
83 pub fn abort_on_err
<T
>(result
: Result
<T
, ErrorGuaranteed
>, sess
: &Session
) -> T
{
86 sess
.abort_if_errors();
87 panic
!("error reported but abort_if_errors didn't abort???");
94 /// Called before creating the compiler instance
95 fn config(&mut self, _config
: &mut interface
::Config
) {}
96 /// Called after parsing. Return value instructs the compiler whether to
97 /// continue the compilation afterwards (defaults to `Compilation::Continue`)
98 fn after_parsing
<'tcx
>(
100 _compiler
: &interface
::Compiler
,
101 _queries
: &'tcx Queries
<'tcx
>,
103 Compilation
::Continue
105 /// Called after expansion. Return value instructs the compiler whether to
106 /// continue the compilation afterwards (defaults to `Compilation::Continue`)
107 fn after_expansion
<'tcx
>(
109 _compiler
: &interface
::Compiler
,
110 _queries
: &'tcx Queries
<'tcx
>,
112 Compilation
::Continue
114 /// Called after analysis. Return value instructs the compiler whether to
115 /// continue the compilation afterwards (defaults to `Compilation::Continue`)
116 fn after_analysis
<'tcx
>(
118 _compiler
: &interface
::Compiler
,
119 _queries
: &'tcx Queries
<'tcx
>,
121 Compilation
::Continue
126 pub struct TimePassesCallbacks
{
130 impl Callbacks
for TimePassesCallbacks
{
131 // JUSTIFICATION: the session doesn't exist at this point.
132 #[allow(rustc::bad_opt_access)]
133 fn config(&mut self, config
: &mut interface
::Config
) {
134 // If a --print=... option has been given, we don't print the "total"
135 // time because it will mess up the --print output. See #64339.
137 self.time_passes
= config
.opts
.prints
.is_empty() && config
.opts
.unstable_opts
.time_passes
;
138 config
.opts
.trimmed_def_paths
= TrimmedDefPaths
::GoodPath
;
142 pub fn diagnostics_registry() -> Registry
{
143 Registry
::new(rustc_error_codes
::DIAGNOSTICS
)
146 /// This is the primary entry point for rustc.
147 pub struct RunCompiler
<'a
, 'b
> {
148 at_args
: &'a
[String
],
149 callbacks
: &'b
mut (dyn Callbacks
+ Send
),
150 file_loader
: Option
<Box
<dyn FileLoader
+ Send
+ Sync
>>,
151 make_codegen_backend
:
152 Option
<Box
<dyn FnOnce(&config
::Options
) -> Box
<dyn CodegenBackend
> + Send
>>,
155 impl<'a
, 'b
> RunCompiler
<'a
, 'b
> {
156 pub fn new(at_args
: &'a
[String
], callbacks
: &'b
mut (dyn Callbacks
+ Send
)) -> Self {
157 Self { at_args, callbacks, file_loader: None, make_codegen_backend: None }
160 /// Set a custom codegen backend.
162 /// Has no uses within this repository, but is used by bjorn3 for "the
163 /// hotswapping branch of cg_clif" for "setting the codegen backend from a
164 /// custom driver where the custom codegen backend has arbitrary data."
166 pub fn set_make_codegen_backend(
168 make_codegen_backend
: Option
<
169 Box
<dyn FnOnce(&config
::Options
) -> Box
<dyn CodegenBackend
> + Send
>,
172 self.make_codegen_backend
= make_codegen_backend
;
176 /// Load files from sources other than the file system.
178 /// Has no uses within this repository, but may be used in the future by
179 /// bjorn3 for "hooking rust-analyzer's VFS into rustc at some point for
180 /// running rustc without having to save". (See #102759.)
181 pub fn set_file_loader(
183 file_loader
: Option
<Box
<dyn FileLoader
+ Send
+ Sync
>>,
185 self.file_loader
= file_loader
;
189 /// Parse args and run the compiler.
190 pub fn run(self) -> interface
::Result
<()> {
191 run_compiler(self.at_args
, self.callbacks
, self.file_loader
, self.make_codegen_backend
)
197 callbacks
: &mut (dyn Callbacks
+ Send
),
198 file_loader
: Option
<Box
<dyn FileLoader
+ Send
+ Sync
>>,
199 make_codegen_backend
: Option
<
200 Box
<dyn FnOnce(&config
::Options
) -> Box
<dyn CodegenBackend
> + Send
>,
202 ) -> interface
::Result
<()> {
203 let args
= args
::arg_expand_all(at_args
);
205 let Some(matches
) = handle_options(&args
) else { return Ok(()) }
;
207 let sopts
= config
::build_session_options(&matches
);
209 if let Some(ref code
) = matches
.opt_str("explain") {
210 handle_explain(diagnostics_registry(), code
, sopts
.error_format
);
214 let cfg
= interface
::parse_cfgspecs(matches
.opt_strs("cfg"));
215 let check_cfg
= interface
::parse_check_cfg(matches
.opt_strs("check-cfg"));
216 let (odir
, ofile
) = make_output(&matches
);
217 let mut config
= interface
::Config
{
220 crate_check_cfg
: check_cfg
,
221 input
: Input
::File(PathBuf
::new()),
225 lint_caps
: Default
::default(),
226 parse_sess_created
: None
,
227 register_lints
: None
,
228 override_queries
: None
,
229 make_codegen_backend
,
230 registry
: diagnostics_registry(),
233 if !tracing
::dispatcher
::has_been_set() {
234 init_rustc_env_logger_with_backtrace_option(&config
.opts
.unstable_opts
.log_backtrace
);
237 match make_input(config
.opts
.error_format
, &matches
.free
) {
238 Err(reported
) => return Err(reported
),
240 config
.input
= input
;
242 callbacks
.config(&mut config
);
244 Ok(None
) => match matches
.free
.len() {
246 callbacks
.config(&mut config
);
247 interface
::run_compiler(config
, |compiler
| {
248 let sopts
= &compiler
.session().opts
;
249 if sopts
.describe_lints
{
251 rustc_lint
::new_lint_store(compiler
.session().enable_internal_lints());
252 let registered_lints
=
253 if let Some(register_lints
) = compiler
.register_lints() {
254 register_lints(compiler
.session(), &mut lint_store
);
259 describe_lints(compiler
.session(), &lint_store
, registered_lints
);
263 print_crate_info(&***compiler
.codegen_backend(), compiler
.session(), false);
265 if should_stop
== Compilation
::Stop
{
268 early_error(sopts
.error_format
, "no input filename given")
272 1 => panic
!("make_input should have provided valid inputs"),
274 config
.opts
.error_format
,
276 "multiple input filenames provided (first two filenames are `{}` and `{}`)",
277 matches
.free
[0], matches
.free
[1],
283 interface
::run_compiler(config
, |compiler
| {
284 let sess
= compiler
.session();
285 let should_stop
= print_crate_info(&***compiler
.codegen_backend(), sess
, true)
286 .and_then(|| list_metadata(sess
, &*compiler
.codegen_backend().metadata_loader()))
287 .and_then(|| try_process_rlink(sess
, compiler
));
289 if should_stop
== Compilation
::Stop
{
290 return sess
.compile_status();
293 let linker
= compiler
.enter(|queries
| {
294 let early_exit
= || sess
.compile_status().map(|_
| None
);
297 if let Some(ppm
) = &sess
.opts
.pretty
{
298 if ppm
.needs_ast_map() {
299 let expanded_crate
= queries
.expansion()?
.borrow().0.clone();
300 queries
.global_ctxt()?
.enter(|tcx
| {
301 pretty
::print_after_hir_lowering(tcx
, &*expanded_crate
, *ppm
);
305 let krate
= queries
.parse()?
.steal();
306 pretty
::print_after_parsing(sess
, &krate
, *ppm
);
308 trace
!("finished pretty-printing");
312 if callbacks
.after_parsing(compiler
, queries
) == Compilation
::Stop
{
316 if sess
.opts
.unstable_opts
.parse_only
|| sess
.opts
.unstable_opts
.show_span
.is_some() {
321 let plugins
= queries
.register_plugins()?
;
322 let (_
, lint_store
) = &*plugins
.borrow();
324 // Lint plugins are registered; now we can process command line flags.
325 if sess
.opts
.describe_lints
{
326 describe_lints(sess
, lint_store
, true);
331 queries
.global_ctxt()?
;
332 if callbacks
.after_expansion(compiler
, queries
) == Compilation
::Stop
{
336 if sess
.opts
.output_types
.contains_key(&OutputType
::DepInfo
)
337 && sess
.opts
.output_types
.len() == 1
342 if sess
.opts
.unstable_opts
.no_analysis
{
346 queries
.global_ctxt()?
.enter(|tcx
| {
347 let result
= tcx
.analysis(());
348 if sess
.opts
.unstable_opts
.save_analysis
{
349 let crate_name
= tcx
.crate_name(LOCAL_CRATE
);
350 sess
.time("save_analysis", || {
356 DumpHandler
::new(sess
.io
.output_dir
.as_deref(), crate_name
),
363 if callbacks
.after_analysis(compiler
, queries
) == Compilation
::Stop
{
367 queries
.ongoing_codegen()?
;
369 if sess
.opts
.unstable_opts
.print_type_sizes
{
370 sess
.code_stats
.print_type_sizes();
373 let linker
= queries
.linker()?
;
377 if let Some(linker
) = linker
{
378 let _timer
= sess
.timer("link");
382 if sess
.opts
.unstable_opts
.perf_stats
{
383 sess
.print_perf_stats();
386 if sess
.opts
.unstable_opts
.print_fuel
.is_some() {
388 "Fuel used by {}: {}",
389 sess
.opts
.unstable_opts
.print_fuel
.as_ref().unwrap(),
390 sess
.print_fuel
.load(SeqCst
)
398 // Extract output directory and file from matches.
399 fn make_output(matches
: &getopts
::Matches
) -> (Option
<PathBuf
>, Option
<PathBuf
>) {
400 let odir
= matches
.opt_str("out-dir").map(|o
| PathBuf
::from(&o
));
401 let ofile
= matches
.opt_str("o").map(|o
| PathBuf
::from(&o
));
405 // Extract input (string or file and optional path) from matches.
407 error_format
: ErrorOutputType
,
408 free_matches
: &[String
],
409 ) -> Result
<Option
<Input
>, ErrorGuaranteed
> {
410 if free_matches
.len() == 1 {
411 let ifile
= &free_matches
[0];
413 let mut src
= String
::new();
414 if io
::stdin().read_to_string(&mut src
).is_err() {
415 // Immediately stop compilation if there was an issue reading
416 // the input (for example if the input stream is not UTF-8).
417 let reported
= early_error_no_abort(
419 "couldn't read from stdin, as it did not contain valid UTF-8",
421 return Err(reported
);
423 if let Ok(path
) = env
::var("UNSTABLE_RUSTDOC_TEST_PATH") {
424 let line
= env
::var("UNSTABLE_RUSTDOC_TEST_LINE").expect(
425 "when UNSTABLE_RUSTDOC_TEST_PATH is set \
426 UNSTABLE_RUSTDOC_TEST_LINE also needs to be set",
428 let line
= isize::from_str_radix(&line
, 10)
429 .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number");
430 let file_name
= FileName
::doc_test_source_code(PathBuf
::from(path
), line
);
431 Ok(Some(Input
::Str { name: file_name, input: src }
))
433 Ok(Some(Input
::Str { name: FileName::anon_source_code(&src), input: src }
))
436 Ok(Some(Input
::File(PathBuf
::from(ifile
))))
443 /// Whether to stop or continue compilation.
444 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
445 pub enum Compilation
{
451 pub fn and_then
<F
: FnOnce() -> Compilation
>(self, next
: F
) -> Compilation
{
453 Compilation
::Stop
=> Compilation
::Stop
,
454 Compilation
::Continue
=> next(),
459 fn handle_explain(registry
: Registry
, code
: &str, output
: ErrorOutputType
) {
460 let upper_cased_code
= code
.to_ascii_uppercase();
462 if upper_cased_code
.starts_with('E'
) { upper_cased_code }
else { format!("E{code:0>4}
") };
463 match registry.try_find_description(&normalised) {
464 Ok(Some(description)) => {
465 let mut is_in_code_block = false;
466 let mut text = String::new();
467 // Slice off the leading newline and print.
468 for line in description.lines() {
470 line.find(|c: char| !c.is_whitespace()).unwrap_or_else(|| line.len());
471 let dedented_line = &line[indent_level..];
472 if dedented_line.starts_with("```
") {
473 is_in_code_block = !is_in_code_block;
474 text.push_str(&line[..(indent_level + 3)]);
475 } else if is_in_code_block && dedented_line.starts_with("# ") {
482 if io::stdout().is_terminal() {
483 show_content_with_pager(&text);
489 early_error(output, &format!("no extended information for {code}"));
491 Err(InvalidErrorCode) => {
492 early_error(output, &format!("{code} is not a valid error code"));
497 fn show_content_with_pager(content: &str) {
498 let pager_name = env::var_os("PAGER").unwrap_or_else(|| {
499 if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") }
502 let mut fallback_to_println = false;
504 match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
506 if let Some(pipe) = pager.stdin.as_mut() {
507 if pipe.write_all(content.as_bytes()).is_err() {
508 fallback_to_println = true;
512 if pager.wait().is_err() {
513 fallback_to_println = true;
517 fallback_to_println = true;
521 // If pager fails for whatever reason, we should still print the content
522 // to standard output
523 if fallback_to_println {
528 pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
529 if sess.opts.unstable_opts.link_only {
530 if let Input::File(file) = &sess.io.input {
531 // FIXME: #![crate_type] and #![crate_name] support not implemented yet
532 sess.init_crate_types(collect_crate_types(sess, &[]));
533 let outputs = compiler.build_output_filenames(sess, &[]);
534 let rlink_data = fs::read(file).unwrap_or_else(|err| {
535 sess.emit_fatal(RlinkUnableToRead { err });
537 let codegen_results = match CodegenResults::deserialize_rlink(rlink_data) {
538 Ok(codegen) => codegen,
541 CodegenErrors::WrongFileType => sess.emit_fatal(RLinkWrongFileType),
542 CodegenErrors::EmptyVersionNumber => {
543 sess.emit_fatal(RLinkEmptyVersionNumber)
545 CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => {
546 sess.emit_fatal(RLinkEncodingVersionMismatch {
551 CodegenErrors::RustcVersionMismatch { rustc_version, current_version } => {
552 sess.emit_fatal(RLinkRustcVersionMismatch {
560 let result = compiler.codegen_backend().link(sess, codegen_results, &outputs);
561 abort_on_err(result, sess);
563 sess.emit_fatal(RlinkNotAFile {})
567 Compilation::Continue
571 pub fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) -> Compilation {
572 if sess.opts.unstable_opts.ls {
573 match sess.io.input {
574 Input::File(ref ifile) => {
575 let path = &(*ifile);
576 let mut v = Vec::new();
577 locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v).unwrap();
578 println!("{}", String::from_utf8(v).unwrap());
580 Input::Str { .. } => {
581 early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
584 return Compilation::Stop;
587 Compilation::Continue
591 codegen_backend: &dyn CodegenBackend,
595 use rustc_session::config::PrintRequest::*;
596 // NativeStaticLibs and LinkArgs are special - printed during linking
597 // (empty iterator returns true)
598 if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) {
599 return Compilation::Continue;
602 let attrs = if parse_attrs {
603 let result = parse_crate_attrs(sess);
605 Ok(attrs) => Some(attrs),
606 Err(mut parse_error) => {
608 return Compilation::Stop;
614 for req in &sess.opts.prints {
617 let mut targets = rustc_target::spec::TARGETS.to_vec();
618 targets.sort_unstable();
619 println!("{}", targets.join("\n"));
621 Sysroot => println!("{}", sess.sysroot.display()),
622 TargetLibdir => println!("{}", sess.target_tlib_path.dir.display()),
624 println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
626 FileNames | CrateName => {
627 let attrs = attrs.as_ref().unwrap();
628 let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess);
629 let id = rustc_session::output::find_crate_name(sess, attrs);
630 if *req == PrintRequest::CrateName {
634 let crate_types = collect_crate_types(sess, attrs);
635 for &style in &crate_types {
637 rustc_session::output::filename_for_input(sess, style, id, &t_outputs);
638 println!("{}", fname.file_name().unwrap().to_string_lossy());
646 .filter_map(|&(name, value)| {
647 // Note that crt-static is a specially recognized cfg
648 // directive that's printed out here as part of
649 // rust-lang/rust#37406, but in general the
650 // `target_feature` cfg is gated under
651 // rust-lang/rust#29717. For now this is just
652 // specifically allowing the crt-static cfg and that's
653 // it, this is intended to get into Cargo and then go
654 // through to build scripts.
655 if (name != sym::target_feature || value != Some(sym::crt_dash_static))
656 && !sess.is_nightly_build()
657 && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some()
662 if let Some(value) = value {
663 Some(format!("{name}=\"{value}\""))
665 Some(name.to_string())
668 .collect::<Vec<String>>();
675 CallingConventions => {
676 let mut calling_conventions = rustc_target::spec::abi::all_names();
677 calling_conventions.sort_unstable();
678 println!("{}", calling_conventions.join("\n"));
684 | StackProtectorStrategies
685 | TargetFeatures => {
686 codegen_backend.print(*req, sess);
688 // Any output here interferes with Cargo's parsing of other printed output
689 NativeStaticLibs => {}
692 use rustc_target::spec::SplitDebuginfo::{Off, Packed, Unpacked};
694 for split in &[Off, Packed, Unpacked] {
695 let stable = sess.target.options.supported_split_debuginfo.contains(split);
696 let unstable_ok = sess.unstable_options();
697 if stable || unstable_ok {
707 /// Prints version information
709 /// NOTE: this is a macro to support drivers built at a different time than the main `rustc_driver` crate.
710 pub macro version($binary: literal, $matches: expr) {
711 fn unw(x: Option<&str>) -> &str {
712 x.unwrap_or("unknown")
714 $crate::version_at_macro_invocation(
717 unw(option_env!("CFG_VERSION")),
718 unw(option_env!("CFG_VER_HASH")),
719 unw(option_env!("CFG_VER_DATE")),
720 unw(option_env!("CFG_RELEASE")),
724 #[doc(hidden)] // use the macro instead
725 pub fn version_at_macro_invocation(
727 matches: &getopts::Matches,
733 let verbose = matches.opt_present("verbose");
735 println!("{binary} {version}");
738 println!("binary: {binary}");
739 println!("commit-hash: {commit_hash}");
740 println!("commit-date: {commit_date}");
741 println!("host: {}", config::host_triple());
742 println!("release: {release}");
744 let debug_flags = matches.opt_strs("Z");
745 let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
746 get_codegen_backend(&None, backend_name).print_version();
750 fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
751 let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() };
752 let mut options = getopts::Options::new();
753 for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) {
754 (option.apply)(&mut options);
756 let message = "Usage: rustc [OPTIONS] INPUT";
757 let nightly_help = if nightly_build {
758 "\n -Z help Print unstable compiler options"
762 let verbose_help = if verbose {
765 "\n --help -v Print the full set of options rustc accepts"
767 let at_path = if verbose {
768 " @path Read newline separated options from `path`\n"
773 "{options}{at_path}\nAdditional help:
774 -C help Print codegen options
776 Print 'lint' options and
default settings{nightly}{verbose}
\n",
777 options = options.usage(message),
779 nightly = nightly_help,
780 verbose = verbose_help
784 fn print_wall_help() {
787 The flag `
-Wall` does not exist
in `rustc`
. Most useful lints are enabled by
788 default. Use `rustc
-W help` to see all available lints
. It's more common to put
789 warning settings
in the
crate root using `
#![warn(LINT_NAME)]` instead of using
790 the command line flag directly
.
795 /// Write to stdout lint command options, together with a list of all available lints
796 pub fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) {
799 Available lint options
:
800 -W
<foo
> Warn about
<foo
>
804 -F
<foo
> Forbid
<foo
> \
805 (deny
<foo
> and all attempts to
override)
810 fn sort_lints(sess: &Session, mut lints: Vec<&'static Lint>) -> Vec<&'static Lint> {
811 // The sort doesn't case-fold but it's doubtful we care.
812 lints.sort_by_cached_key(|x: &&Lint| (x.default_level(sess.edition()), x.name));
817 lints: Vec<(&'static str, Vec<LintId>, bool)>,
818 ) -> Vec<(&'static str, Vec<LintId>)> {
819 let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect();
820 lints.sort_by_key(|l| l.0);
824 let (plugin, builtin): (Vec<_>, _) =
825 lint_store.get_lints().iter().cloned().partition(|&lint| lint.is_plugin);
826 let plugin = sort_lints(sess, plugin);
827 let builtin = sort_lints(sess, builtin);
829 let (plugin_groups, builtin_groups): (Vec<_>, _) =
830 lint_store.get_lint_groups().partition(|&(.., p)| p);
831 let plugin_groups = sort_lint_groups(plugin_groups);
832 let builtin_groups = sort_lint_groups(builtin_groups);
835 plugin.iter().chain(&builtin).map(|&s| s.name.chars().count()).max().unwrap_or(0);
836 let padded = |x: &str| {
837 let mut s = " ".repeat(max_name_len - x.chars().count());
842 println!("Lint checks provided by rustc
:\n");
844 let print_lints = |lints: Vec<&Lint>| {
845 println!(" {} {:7.7} {}
", padded("name
"), "default", "meaning
");
846 println!(" {} {:7.7} {}
", padded("----"), "-------", "-------");
848 let name = lint.name_lower().replace('_', "-");
852 lint.default_level(sess.edition()).as_str(),
859 print_lints(builtin);
861 let max_name_len = max(
865 .chain(&builtin_groups)
866 .map(|&(s, _)| s.chars().count())
871 let padded = |x: &str| {
872 let mut s = " ".repeat(max_name_len - x.chars().count());
877 println!("Lint groups provided by rustc
:\n");
879 let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>, all_warnings| {
880 println!(" {} sub
-lints
", padded("name
"));
881 println!(" {}
---------", padded("----"));
884 println!(" {} all lints that are set to issue warnings
", padded("warnings
"));
887 for (name, to) in lints {
888 let name = name.to_lowercase().replace('_', "-");
891 .map(|x| x.to_string().replace('_', "-"))
892 .collect::<Vec<String>>()
894 println!(" {} {}
", padded(&name), desc);
899 print_lint_groups(builtin_groups, true);
901 match (loaded_plugins, plugin.len(), plugin_groups.len()) {
902 (false, 0, _) | (false, _, 0) => {
903 println!("Lint tools like Clippy can provide additional lints and lint groups
.");
905 (false, ..) => panic!("didn't load lint plugins but got them anyway
!"),
906 (true, 0, 0) => println!("This
crate does not load any lint plugins or lint groups
."),
909 println!("Lint checks provided by plugins loaded by this
crate:\n");
913 println!("Lint groups provided by plugins loaded by this
crate:\n");
914 print_lint_groups(plugin_groups, false);
920 fn describe_debug_flags() {
921 println!("\nAvailable options
:\n");
922 print_flag_list("-Z
", config::Z_OPTIONS);
925 fn describe_codegen_flags() {
926 println!("\nAvailable codegen options
:\n");
927 print_flag_list("-C
", config::CG_OPTIONS);
930 pub fn print_flag_list<T>(
932 flag_list: &[(&'static str, T, &'static str, &'static str)],
934 let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0);
936 for &(name, _, _, desc) in flag_list {
938 " {} {:>width$}
=val
-- {}
",
940 name.replace('_', "-"),
947 /// Process command line options. Emits messages as appropriate. If compilation
948 /// should continue, returns a getopts::Matches object parsed from args,
949 /// otherwise returns `None`.
951 /// The compiler's handling of options is a little complicated as it ties into
952 /// our stability story. The current intention of each compiler option is to
953 /// have one of two modes:
955 /// 1. An option is stable and can be used everywhere.
956 /// 2. An option is unstable, and can only be used on nightly.
958 /// Like unstable library and language features, however, unstable options have
959 /// always required a form of "opt
in" to indicate that you're using them. This
960 /// provides the easy ability to scan a code base to check to see if anything
961 /// unstable is being used. Currently, this "opt
in" is the `-Z` "zed
" flag.
963 /// All options behind `-Z` are considered unstable by default. Other top-level
964 /// options can also be considered unstable, and they were unlocked through the
965 /// `-Z unstable-options` flag. Note that `-Z` remains to be the root of
966 /// instability in both cases, though.
968 /// So with all that in mind, the comments below have some more detail about the
969 /// contortions done here to get things to work out correctly.
970 pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
971 // Throw away the first argument, the name of the binary
972 let args = &args[1..];
975 // user did not write `-v` nor `-Z unstable-options`, so do not
976 // include that extra information.
978 rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build();
979 usage(false, false, nightly_build);
983 // Parse with *all* options defined in the compiler, we don't worry about
984 // option stability here we just want to parse as much as possible.
985 let mut options = getopts::Options::new();
986 for option in config::rustc_optgroups() {
987 (option.apply)(&mut options);
989 let matches = options.parse(args).unwrap_or_else(|e| {
991 getopts::Fail::UnrecognizedOption(ref opt) => CG_OPTIONS
993 .map(|&(name, ..)| ('C', name))
994 .chain(Z_OPTIONS.iter().map(|&(name, ..)| ('Z', name)))
995 .find(|&(_, name)| *opt == name.replace('_', "-"))
996 .map(|(flag, _)| format!("{e}
. Did you mean `
-{flag} {opt}`?
")),
999 early_error(ErrorOutputType::default(), &msg.unwrap_or_else(|| e.to_string()));
1002 // For all options we just parsed, we check a few aspects:
1004 // * If the option is stable, we're all good
1005 // * If the option wasn't passed, we're all good
1006 // * If `-Z unstable-options` wasn't passed (and we're not a -Z option
1007 // ourselves), then we require the `-Z unstable-options` flag to unlock
1008 // this option that was passed.
1009 // * If we're a nightly compiler, then unstable options are now unlocked, so
1010 // we're good to go.
1011 // * Otherwise, if we're an unstable option then we generate an error
1012 // (unstable option being used on stable)
1013 nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
1015 if matches.opt_present("h
") || matches.opt_present("help
") {
1016 // Only show unstable options in --help if we accept unstable options.
1017 let unstable_enabled = nightly_options::is_unstable_enabled(&matches);
1018 let nightly_build = nightly_options::match_is_nightly_build(&matches);
1019 usage(matches.opt_present("verbose
"), unstable_enabled, nightly_build);
1023 // Handle the special case of -Wall.
1024 let wall = matches.opt_strs("W
");
1025 if wall.iter().any(|x| *x == "all
") {
1027 rustc_errors::FatalError.raise();
1030 // Don't handle -W help here, because we might first load plugins.
1031 let debug_flags = matches.opt_strs("Z
");
1032 if debug_flags.iter().any(|x| *x == "help
") {
1033 describe_debug_flags();
1037 let cg_flags = matches.opt_strs("C
");
1039 if cg_flags.iter().any(|x| *x == "help
") {
1040 describe_codegen_flags();
1044 if cg_flags.iter().any(|x| *x == "no
-stack
-check
") {
1046 ErrorOutputType::default(),
1047 "the
--no
-stack
-check flag is deprecated and does nothing
",
1051 if cg_flags.iter().any(|x| *x == "passes
=list
") {
1052 let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen
-backend
="));
1053 get_codegen_backend(&None, backend_name).print_passes();
1057 if matches.opt_present("version
") {
1058 version!("rustc
", &matches);
1065 fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
1066 match &sess.io.input {
1067 Input::File(ifile) => rustc_parse::parse_crate_attrs_from_file(ifile, &sess.parse_sess),
1068 Input::Str { name, input } => rustc_parse::parse_crate_attrs_from_source_str(
1076 /// Gets a list of extra command-line flags provided by the user, as strings.
1078 /// This function is used during ICEs to show more information useful for
1079 /// debugging, since some ICEs only happens with non-default compiler flags
1080 /// (and the users don't always report them).
1081 fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
1082 let mut args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).peekable();
1084 let mut result = Vec::new();
1085 let mut excluded_cargo_defaults = false;
1086 while let Some(arg) = args.next() {
1087 if let Some(a) = ICE_REPORT_COMPILER_FLAGS.iter().find(|a| arg.starts_with(*a)) {
1088 let content = if arg.len() == a.len() {
1089 // A space-separated option, like `-C incremental=foo` or `--crate-type rlib`
1091 Some(arg) => arg.to_string(),
1094 } else if arg.get(a.len()..a.len() + 1) == Some("=") {
1095 // An equals option, like `--crate-type=rlib`
1096 arg[a.len() + 1..].to_string()
1098 // A non-space option, like `-Cincremental=foo`
1099 arg[a.len()..].to_string()
1101 let option = content.split_once('=').map(|s| s.0).unwrap_or(&content);
1102 if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.iter().any(|exc| option == *exc) {
1103 excluded_cargo_defaults = true;
1105 result.push(a.to_string());
1106 match ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.iter().find(|s| option == **s) {
1107 Some(s) => result.push(format!("{s}
=[REDACTED
]")),
1108 None => result.push(content),
1114 if !result.is_empty() { Some((result, excluded_cargo_defaults)) } else { None }
1117 /// Runs a closure and catches unwinds triggered by fatal errors.
1119 /// The compiler currently unwinds with a special sentinel value to abort
1120 /// compilation on fatal errors. This function catches that sentinel and turns
1121 /// the panic into a `Result` instead.
1122 pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, ErrorGuaranteed> {
1123 catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| {
1124 if value.is::<rustc_errors::FatalErrorMarker>() {
1125 ErrorGuaranteed::unchecked_claim_error_was_emitted()
1127 panic::resume_unwind(value);
1132 /// Variant of `catch_fatal_errors` for the `interface::Result` return type
1133 /// that also computes the exit code.
1134 pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
1135 let result = catch_fatal_errors(f).and_then(|result| result);
1137 Ok(()) => EXIT_SUCCESS,
1138 Err(_) => EXIT_FAILURE,
1142 static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
1144 let hook = panic::take_hook();
1145 panic::set_hook(Box::new(|info| {
1146 // If the error was caused by a broken pipe then this is not a bug.
1147 // Write the error and return immediately. See #98700.
1149 if let Some(msg) = info.payload().downcast_ref::<String>() {
1150 if msg.starts_with("failed printing to stdout
: ") && msg.ends_with("(os error
232)")
1152 early_error_no_abort(ErrorOutputType::default(), &msg);
1157 // Invoke the default handler, which prints the actual panic message and optionally a backtrace
1158 // Don't do this for delayed bugs, which already emit their own more useful backtrace.
1159 if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
1160 (*DEFAULT_HOOK)(info);
1162 // Separate the output with an empty line
1166 // Print the ICE message
1167 report_ice(info, BUG_REPORT_URL);
1172 /// Prints the ICE message, including query stack, but without backtrace.
1174 /// The message will point the user at `bug_report_url` to report the ICE.
1176 /// When `install_ice_hook` is called, this function will be called as the panic
1178 pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
1179 let fallback_bundle =
1180 rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
1181 let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
1182 rustc_errors::ColorConfig::Auto,
1192 let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
1194 // a .span_bug or .bug call has already printed what
1195 // it wants to print.
1196 if !info.payload().is::<rustc_errors::ExplicitBug>()
1197 && !info.payload().is::<rustc_errors::DelayedBugPanic>()
1199 let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic
");
1200 handler.emit_diagnostic(&mut d);
1203 let mut xs: Vec<Cow<'static, str>> = vec![
1204 "the compiler unexpectedly panicked
. this is a bug
.".into(),
1205 format!("we would appreciate a bug report
: {bug_report_url}
").into(),
1207 "rustc {} running on {}
",
1208 util::version_str!().unwrap_or("unknown_version
"),
1209 config::host_triple()
1214 if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
1215 xs.push(format!("compiler flags
: {}
", flags.join(" ")).into());
1217 if excluded_cargo_defaults {
1218 xs.push("some of the compiler flags provided by cargo are hidden
".into());
1223 handler.note_without_error(note.as_ref());
1226 // If backtraces are enabled, also print the query stack
1227 let backtrace = env::var_os("RUST_BACKTRACE
").map_or(false, |x| &x != "0");
1229 let num_frames = if backtrace { None } else { Some(2) };
1231 interface::try_print_query_stack(&handler, num_frames);
1235 if env::var("RUSTC_BREAK_ON_ICE
").is_ok() {
1236 // Trigger a debugger if we crashed during bootstrap
1237 winapi::um::debugapi::DebugBreak();
1242 /// Installs a panic hook that will print the ICE message on unexpected panics.
1244 /// A custom rustc driver can skip calling this to set up a custom ICE hook.
1245 pub fn install_ice_hook() {
1246 // If the user has not explicitly overridden "RUST_BACKTRACE
", then produce
1247 // full backtraces. When a compiler ICE happens, we want to gather
1248 // as much information as possible to present in the issue opened
1249 // by the user. Compiler developers and other rustc users can
1250 // opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE
"
1251 // (e.g. `RUST_BACKTRACE=1`)
1252 if std::env::var("RUST_BACKTRACE
").is_err() {
1253 std::env::set_var("RUST_BACKTRACE
", "full
");
1255 LazyLock::force(&DEFAULT_HOOK);
1258 /// This allows tools to enable rust logging without having to magically match rustc's
1259 /// tracing crate version.
1260 pub fn init_rustc_env_logger() {
1261 init_rustc_env_logger_with_backtrace_option(&None);
1264 /// This allows tools to enable rust logging without having to magically match rustc's
1265 /// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to
1266 /// choose a target module you wish to show backtraces along with its logging.
1267 pub fn init_rustc_env_logger_with_backtrace_option(backtrace_target: &Option<String>) {
1268 if let Err(error) = rustc_log::init_rustc_env_logger_with_backtrace_option(backtrace_target) {
1269 early_error(ErrorOutputType::default(), &error.to_string());
1273 /// This allows tools to enable rust logging without having to magically match rustc's
1274 /// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose an env var
1275 /// other than `RUSTC_LOG`.
1276 pub fn init_env_logger(env: &str) {
1277 if let Err(error) = rustc_log::init_env_logger(env) {
1278 early_error(ErrorOutputType::default(), &error.to_string());
1282 #[cfg(all(unix, any(target_env = "gnu
", target_os = "macos
")))]
1283 mod signal_handler {
1285 fn backtrace_symbols_fd(
1286 buffer: *const *mut libc::c_void,
1292 extern "C
" fn print_stack_trace(_: libc::c_int) {
1293 const MAX_FRAMES: usize = 256;
1294 static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] =
1295 [std::ptr::null_mut(); MAX_FRAMES];
1297 let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32);
1301 backtrace_symbols_fd(STACK_TRACE.as_ptr(), depth, 2);
1305 /// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the
1306 /// process, print a stack trace and then exit.
1307 pub(super) fn install() {
1309 const ALT_STACK_SIZE: usize = libc::MINSIGSTKSZ + 64 * 1024;
1310 let mut alt_stack: libc::stack_t = std::mem::zeroed();
1312 std::alloc::alloc(std::alloc::Layout::from_size_align(ALT_STACK_SIZE, 1).unwrap())
1313 as *mut libc::c_void;
1314 alt_stack.ss_size = ALT_STACK_SIZE;
1315 libc::sigaltstack(&alt_stack, std::ptr::null_mut());
1317 let mut sa: libc::sigaction = std::mem::zeroed();
1318 sa.sa_sigaction = print_stack_trace as libc::sighandler_t;
1319 sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK;
1320 libc::sigemptyset(&mut sa.sa_mask);
1321 libc::sigaction(libc::SIGSEGV, &sa, std::ptr::null_mut());
1326 #[cfg(not(all(unix, any(target_env = "gnu
", target_os = "macos
"))))]
1327 mod signal_handler {
1328 pub(super) fn install() {}
1331 pub fn main() -> ! {
1332 let start_time = Instant::now();
1333 let start_rss = get_resident_set_size();
1334 signal_handler::install();
1335 let mut callbacks = TimePassesCallbacks::default();
1337 let exit_code = catch_with_exit_code(|| {
1338 let args = env::args_os()
1341 arg.into_string().unwrap_or_else(|arg| {
1343 ErrorOutputType::default(),
1344 &format!("argument {i} is not valid Unicode
: {arg:?}
"),
1348 .collect::<Vec<_>>();
1349 RunCompiler::new(&args, &mut callbacks).run()
1352 if callbacks.time_passes {
1353 let end_rss = get_resident_set_size();
1354 print_time_passes_entry("total
", start_time.elapsed(), start_rss, end_rss);
1357 process::exit(exit_code)