]> git.lizzy.rs Git - rust.git/blob - src/librustc_driver/lib.rs
b461431c7bbbd74b6eb6a8d66f6ccf88910ebd11
[rust.git] / src / librustc_driver / lib.rs
1 // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! The Rust compiler.
12 //!
13 //! # Note
14 //!
15 //! This API is completely unstable and subject to change.
16
17 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
18       html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
19       html_root_url = "https://doc.rust-lang.org/nightly/")]
20 #![deny(warnings)]
21
22 #![feature(box_syntax)]
23 #![cfg_attr(unix, feature(libc))]
24 #![feature(quote)]
25 #![feature(rustc_diagnostic_macros)]
26 #![feature(set_stdio)]
27 #![feature(rustc_stack_internals)]
28
29 extern crate arena;
30 extern crate getopts;
31 extern crate graphviz;
32 extern crate env_logger;
33 #[cfg(unix)]
34 extern crate libc;
35 extern crate rustc;
36 extern crate rustc_allocator;
37 extern crate rustc_back;
38 extern crate rustc_borrowck;
39 extern crate rustc_data_structures;
40 extern crate rustc_errors as errors;
41 extern crate rustc_passes;
42 extern crate rustc_lint;
43 extern crate rustc_plugin;
44 extern crate rustc_privacy;
45 extern crate rustc_incremental;
46 extern crate rustc_metadata;
47 extern crate rustc_mir;
48 extern crate rustc_resolve;
49 extern crate rustc_save_analysis;
50 extern crate rustc_traits;
51 extern crate rustc_trans_utils;
52 extern crate rustc_typeck;
53 extern crate serialize;
54 #[macro_use]
55 extern crate log;
56 extern crate syntax;
57 extern crate syntax_ext;
58 extern crate syntax_pos;
59
60 use driver::CompileController;
61 use pretty::{PpMode, UserIdentifiedItem};
62
63 use rustc_resolve as resolve;
64 use rustc_save_analysis as save;
65 use rustc_save_analysis::DumpHandler;
66 use rustc_data_structures::sync::Lrc;
67 use rustc_data_structures::OnDrop;
68 use rustc::session::{self, config, Session, build_session, CompileResult};
69 use rustc::session::CompileIncomplete;
70 use rustc::session::config::{Input, PrintRequest, ErrorOutputType};
71 use rustc::session::config::nightly_options;
72 use rustc::session::filesearch;
73 use rustc::session::{early_error, early_warn};
74 use rustc::lint::Lint;
75 use rustc::lint;
76 use rustc::middle::cstore::CrateStore;
77 use rustc_metadata::locator;
78 use rustc_metadata::cstore::CStore;
79 use rustc_metadata::dynamic_lib::DynamicLibrary;
80 use rustc::util::common::{time, ErrorReported};
81 use rustc_trans_utils::trans_crate::TransCrate;
82
83 use serialize::json::ToJson;
84
85 use std::any::Any;
86 use std::cmp::Ordering::Equal;
87 use std::cmp::max;
88 use std::default::Default;
89 use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
90 use std::env;
91 use std::ffi::OsString;
92 use std::io::{self, Read, Write};
93 use std::iter::repeat;
94 use std::mem;
95 use std::panic;
96 use std::path::{PathBuf, Path};
97 use std::process::{self, Command, Stdio};
98 use std::str;
99 use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
100 use std::sync::{Once, ONCE_INIT};
101 use std::thread;
102
103 use syntax::ast;
104 use syntax::codemap::{CodeMap, FileLoader, RealFileLoader};
105 use syntax::feature_gate::{GatedCfg, UnstableFeatures};
106 use syntax::parse::{self, PResult};
107 use syntax_pos::{DUMMY_SP, MultiSpan, FileName};
108
109 #[cfg(test)]
110 mod test;
111
112 pub mod profile;
113 pub mod driver;
114 pub mod pretty;
115 mod derive_registrar;
116
117 pub mod target_features {
118     use syntax::ast;
119     use syntax::symbol::Symbol;
120     use rustc::session::Session;
121     use rustc_trans_utils::trans_crate::TransCrate;
122
123     /// Add `target_feature = "..."` cfgs for a variety of platform
124     /// specific features (SSE, NEON etc.).
125     ///
126     /// This is performed by checking whether a whitelisted set of
127     /// features is available on the target machine, by querying LLVM.
128     pub fn add_configuration(cfg: &mut ast::CrateConfig, sess: &Session, trans: &TransCrate) {
129         let tf = Symbol::intern("target_feature");
130
131         for feat in trans.target_features(sess) {
132             cfg.insert((tf, Some(feat)));
133         }
134
135         if sess.crt_static_feature() {
136             cfg.insert((tf, Some(Symbol::intern("crt-static"))));
137         }
138     }
139 }
140
141 const BUG_REPORT_URL: &'static str = "https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.\
142                                       md#bug-reports";
143
144 const ICE_REPORT_COMPILER_FLAGS: &'static [&'static str] = &[
145     "Z",
146     "C",
147     "crate-type",
148 ];
149 const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &'static [&'static str] = &[
150     "metadata",
151     "extra-filename",
152 ];
153 const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &'static [&'static str] = &[
154     "incremental",
155 ];
156
157 pub fn abort_on_err<T>(result: Result<T, CompileIncomplete>, sess: &Session) -> T {
158     match result {
159         Err(CompileIncomplete::Errored(ErrorReported)) => {
160             sess.abort_if_errors();
161             panic!("error reported but abort_if_errors didn't abort???");
162         }
163         Err(CompileIncomplete::Stopped) => {
164             sess.fatal("compilation terminated");
165         }
166         Ok(x) => x,
167     }
168 }
169
170 pub fn run<F>(run_compiler: F) -> isize
171     where F: FnOnce() -> (CompileResult, Option<Session>) + Send + 'static
172 {
173     monitor(move || {
174         let (result, session) = run_compiler();
175         if let Err(CompileIncomplete::Errored(_)) = result {
176             match session {
177                 Some(sess) => {
178                     sess.abort_if_errors();
179                     panic!("error reported but abort_if_errors didn't abort???");
180                 }
181                 None => {
182                     let emitter =
183                         errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto,
184                                                                None,
185                                                                true,
186                                                                false);
187                     let handler = errors::Handler::with_emitter(true, false, Box::new(emitter));
188                     handler.emit(&MultiSpan::new(),
189                                  "aborting due to previous error(s)",
190                                  errors::Level::Fatal);
191                     panic::resume_unwind(Box::new(errors::FatalErrorMarker));
192                 }
193             }
194         }
195     });
196     0
197 }
198
199 fn load_backend_from_dylib(path: &Path) -> fn() -> Box<TransCrate> {
200     // Note that we're specifically using `open_global_now` here rather than
201     // `open`, namely we want the behavior on Unix of RTLD_GLOBAL and RTLD_NOW,
202     // where NOW means "bind everything right now" because we don't want
203     // surprises later on and RTLD_GLOBAL allows the symbols to be made
204     // available for future dynamic libraries opened. This is currently used by
205     // loading LLVM and then making its symbols available for other dynamic
206     // libraries.
207     let lib = match DynamicLibrary::open_global_now(path) {
208         Ok(lib) => lib,
209         Err(err) => {
210             let err = format!("couldn't load codegen backend {:?}: {:?}",
211                               path,
212                               err);
213             early_error(ErrorOutputType::default(), &err);
214         }
215     };
216     unsafe {
217         match lib.symbol("__rustc_codegen_backend") {
218             Ok(f) => {
219                 mem::forget(lib);
220                 mem::transmute::<*mut u8, _>(f)
221             }
222             Err(e) => {
223                 let err = format!("couldn't load codegen backend as it \
224                                    doesn't export the `__rustc_codegen_backend` \
225                                    symbol: {:?}", e);
226                 early_error(ErrorOutputType::default(), &err);
227             }
228         }
229     }
230 }
231
232 pub fn get_trans(sess: &Session) -> Box<TransCrate> {
233     static INIT: Once = ONCE_INIT;
234     static mut LOAD: fn() -> Box<TransCrate> = || unreachable!();
235
236     INIT.call_once(|| {
237         let trans_name = sess.opts.debugging_opts.codegen_backend.as_ref()
238             .unwrap_or(&sess.target.target.options.codegen_backend);
239         let backend = match &trans_name[..] {
240             "metadata_only" => {
241                 rustc_trans_utils::trans_crate::MetadataOnlyTransCrate::new
242             }
243             filename if filename.contains(".") => {
244                 load_backend_from_dylib(filename.as_ref())
245             }
246             trans_name => get_trans_sysroot(trans_name),
247         };
248
249         unsafe {
250             LOAD = backend;
251         }
252     });
253     let backend = unsafe { LOAD() };
254     backend.init(sess);
255     backend
256 }
257
258 fn get_trans_sysroot(backend_name: &str) -> fn() -> Box<TransCrate> {
259     // For now we only allow this function to be called once as it'll dlopen a
260     // few things, which seems to work best if we only do that once. In
261     // general this assertion never trips due to the once guard in `get_trans`,
262     // but there's a few manual calls to this function in this file we protect
263     // against.
264     static LOADED: AtomicBool = ATOMIC_BOOL_INIT;
265     assert!(!LOADED.fetch_or(true, Ordering::SeqCst),
266             "cannot load the default trans backend twice");
267
268     // When we're compiling this library with `--test` it'll run as a binary but
269     // not actually exercise much functionality. As a result most of the logic
270     // here is defunkt (it assumes we're a dynamic library in a sysroot) so
271     // let's just return a dummy creation function which won't be used in
272     // general anyway.
273     if cfg!(test) {
274         return rustc_trans_utils::trans_crate::MetadataOnlyTransCrate::new
275     }
276
277     let target = session::config::host_triple();
278     let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()];
279     let path = current_dll_path()
280         .and_then(|s| s.canonicalize().ok());
281     if let Some(dll) = path {
282         // use `parent` twice to chop off the file name and then also the
283         // directory containing the dll which should be either `lib` or `bin`.
284         if let Some(path) = dll.parent().and_then(|p| p.parent()) {
285             // The original `path` pointed at the `rustc_driver` crate's dll.
286             // Now that dll should only be in one of two locations. The first is
287             // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The
288             // other is the target's libdir, for example
289             // `$sysroot/lib/rustlib/$target/lib/*.dll`.
290             //
291             // We don't know which, so let's assume that if our `path` above
292             // ends in `$target` we *could* be in the target libdir, and always
293             // assume that we may be in the main libdir.
294             sysroot_candidates.push(path.to_owned());
295
296             if path.ends_with(target) {
297                 sysroot_candidates.extend(path.parent() // chop off `$target`
298                     .and_then(|p| p.parent())           // chop off `rustlib`
299                     .and_then(|p| p.parent())           // chop off `lib`
300                     .map(|s| s.to_owned()));
301             }
302         }
303     }
304
305     let sysroot = sysroot_candidates.iter()
306         .map(|sysroot| {
307             let libdir = filesearch::relative_target_lib_path(&sysroot, &target);
308             sysroot.join(libdir)
309                 .with_file_name(option_env!("CFG_CODEGEN_BACKENDS_DIR")
310                                 .unwrap_or("codegen-backends"))
311         })
312         .filter(|f| {
313             info!("codegen backend candidate: {}", f.display());
314             f.exists()
315         })
316         .next();
317     let sysroot = match sysroot {
318         Some(path) => path,
319         None => {
320             let candidates = sysroot_candidates.iter()
321                 .map(|p| p.display().to_string())
322                 .collect::<Vec<_>>()
323                 .join("\n* ");
324             let err = format!("failed to find a `codegen-backends` folder \
325                                in the sysroot candidates:\n* {}", candidates);
326             early_error(ErrorOutputType::default(), &err);
327         }
328     };
329     info!("probing {} for a codegen backend", sysroot.display());
330
331     let d = match sysroot.read_dir() {
332         Ok(d) => d,
333         Err(e) => {
334             let err = format!("failed to load default codegen backend, couldn't \
335                                read `{}`: {}", sysroot.display(), e);
336             early_error(ErrorOutputType::default(), &err);
337         }
338     };
339
340     let mut file: Option<PathBuf> = None;
341
342     let expected_name = format!("rustc_trans-{}", backend_name);
343     for entry in d.filter_map(|e| e.ok()) {
344         let path = entry.path();
345         let filename = match path.file_name().and_then(|s| s.to_str()) {
346             Some(s) => s,
347             None => continue,
348         };
349         if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) {
350             continue
351         }
352         let name = &filename[DLL_PREFIX.len() .. filename.len() - DLL_SUFFIX.len()];
353         if name != expected_name {
354             continue
355         }
356         if let Some(ref prev) = file {
357             let err = format!("duplicate codegen backends found\n\
358                 first:  {}\n\
359                 second: {}\n\
360             ", prev.display(), path.display());
361             early_error(ErrorOutputType::default(), &err);
362         }
363         file = Some(path.clone());
364     }
365
366     match file {
367         Some(ref s) => return load_backend_from_dylib(s),
368         None => {
369             let err = format!("failed to load default codegen backend for `{}`, \
370                                no appropriate codegen dylib found in `{}`",
371                                backend_name, sysroot.display());
372             early_error(ErrorOutputType::default(), &err);
373         }
374     }
375
376     #[cfg(unix)]
377     fn current_dll_path() -> Option<PathBuf> {
378         use std::ffi::{OsStr, CStr};
379         use std::os::unix::prelude::*;
380
381         unsafe {
382             let addr = current_dll_path as usize as *mut _;
383             let mut info = mem::zeroed();
384             if libc::dladdr(addr, &mut info) == 0 {
385                 info!("dladdr failed");
386                 return None
387             }
388             if info.dli_fname.is_null() {
389                 info!("dladdr returned null pointer");
390                 return None
391             }
392             let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
393             let os = OsStr::from_bytes(bytes);
394             Some(PathBuf::from(os))
395         }
396     }
397
398     #[cfg(windows)]
399     fn current_dll_path() -> Option<PathBuf> {
400         use std::ffi::OsString;
401         use std::os::windows::prelude::*;
402
403         extern "system" {
404             fn GetModuleHandleExW(dwFlags: u32,
405                                   lpModuleName: usize,
406                                   phModule: *mut usize) -> i32;
407             fn GetModuleFileNameW(hModule: usize,
408                                   lpFilename: *mut u16,
409                                   nSize: u32) -> u32;
410         }
411
412         const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 0x00000004;
413
414         unsafe {
415             let mut module = 0;
416             let r = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
417                                        current_dll_path as usize,
418                                        &mut module);
419             if r == 0 {
420                 info!("GetModuleHandleExW failed: {}", io::Error::last_os_error());
421                 return None
422             }
423             let mut space = Vec::with_capacity(1024);
424             let r = GetModuleFileNameW(module,
425                                        space.as_mut_ptr(),
426                                        space.capacity() as u32);
427             if r == 0 {
428                 info!("GetModuleFileNameW failed: {}", io::Error::last_os_error());
429                 return None
430             }
431             let r = r as usize;
432             if r >= space.capacity() {
433                 info!("our buffer was too small? {}",
434                       io::Error::last_os_error());
435                 return None
436             }
437             space.set_len(r);
438             let os = OsString::from_wide(&space);
439             Some(PathBuf::from(os))
440         }
441     }
442 }
443
444 // Parse args and run the compiler. This is the primary entry point for rustc.
445 // See comments on CompilerCalls below for details about the callbacks argument.
446 // The FileLoader provides a way to load files from sources other than the file system.
447 pub fn run_compiler<'a>(args: &[String],
448                         callbacks: &mut CompilerCalls<'a>,
449                         file_loader: Option<Box<FileLoader + Send + Sync + 'static>>,
450                         emitter_dest: Option<Box<Write + Send>>)
451                         -> (CompileResult, Option<Session>)
452 {
453     syntax::with_globals(|| {
454         run_compiler_impl(args, callbacks, file_loader, emitter_dest)
455     })
456 }
457
458 fn run_compiler_impl<'a>(args: &[String],
459                          callbacks: &mut CompilerCalls<'a>,
460                          file_loader: Option<Box<FileLoader + Send + Sync + 'static>>,
461                          emitter_dest: Option<Box<Write + Send>>)
462                          -> (CompileResult, Option<Session>)
463 {
464     macro_rules! do_or_return {($expr: expr, $sess: expr) => {
465         match $expr {
466             Compilation::Stop => return (Ok(()), $sess),
467             Compilation::Continue => {}
468         }
469     }}
470
471     let matches = match handle_options(args) {
472         Some(matches) => matches,
473         None => return (Ok(()), None),
474     };
475
476     let (sopts, cfg) = config::build_session_options_and_crate_config(&matches);
477
478     let descriptions = diagnostics_registry();
479
480     do_or_return!(callbacks.early_callback(&matches,
481                                            &sopts,
482                                            &cfg,
483                                            &descriptions,
484                                            sopts.error_format),
485                                            None);
486
487     let (odir, ofile) = make_output(&matches);
488     let (input, input_file_path, input_err) = match make_input(&matches.free) {
489         Some((input, input_file_path, input_err)) => {
490             let (input, input_file_path) = callbacks.some_input(input, input_file_path);
491             (input, input_file_path, input_err)
492         },
493         None => match callbacks.no_input(&matches, &sopts, &cfg, &odir, &ofile, &descriptions) {
494             Some((input, input_file_path)) => (input, input_file_path, None),
495             None => return (Ok(()), None),
496         },
497     };
498
499     let loader = file_loader.unwrap_or(box RealFileLoader);
500     let codemap = Lrc::new(CodeMap::with_file_loader(loader, sopts.file_path_mapping()));
501     let mut sess = session::build_session_with_codemap(
502         sopts, input_file_path.clone(), descriptions, codemap, emitter_dest,
503     );
504
505     if let Some(err) = input_err {
506         // Immediately stop compilation if there was an issue reading
507         // the input (for example if the input stream is not UTF-8).
508         sess.err(&format!("{}", err));
509         return (Err(CompileIncomplete::Stopped), Some(sess));
510     }
511
512     let trans = get_trans(&sess);
513
514     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
515
516     let mut cfg = config::build_configuration(&sess, cfg);
517     target_features::add_configuration(&mut cfg, &sess, &*trans);
518     sess.parse_sess.config = cfg;
519
520     let result = {
521         let plugins = sess.opts.debugging_opts.extra_plugins.clone();
522
523         let cstore = CStore::new(trans.metadata_loader());
524
525         do_or_return!(callbacks.late_callback(&*trans,
526                                               &matches,
527                                               &sess,
528                                               &cstore,
529                                               &input,
530                                               &odir,
531                                               &ofile), Some(sess));
532
533         let _sess_abort_error = OnDrop(|| sess.diagnostic().print_error_count());
534
535         let control = callbacks.build_controller(&sess, &matches);
536
537         driver::compile_input(trans,
538                               &sess,
539                               &cstore,
540                               &input_file_path,
541                               &input,
542                               &odir,
543                               &ofile,
544                               Some(plugins),
545                               &control)
546     };
547
548     (result, Some(sess))
549 }
550
551 // Extract output directory and file from matches.
552 fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<PathBuf>) {
553     let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));
554     let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o));
555     (odir, ofile)
556 }
557
558 // Extract input (string or file and optional path) from matches.
559 fn make_input(free_matches: &[String]) -> Option<(Input, Option<PathBuf>, Option<io::Error>)> {
560     if free_matches.len() == 1 {
561         let ifile = &free_matches[0];
562         if ifile == "-" {
563             let mut src = String::new();
564             let err = if io::stdin().read_to_string(&mut src).is_err() {
565                 Some(io::Error::new(io::ErrorKind::InvalidData,
566                                     "couldn't read from stdin, as it did not contain valid UTF-8"))
567             } else {
568                 None
569             };
570             Some((Input::Str { name: FileName::Anon, input: src },
571                   None, err))
572         } else {
573             Some((Input::File(PathBuf::from(ifile)),
574                   Some(PathBuf::from(ifile)), None))
575         }
576     } else {
577         None
578     }
579 }
580
581 fn parse_pretty(sess: &Session,
582                 matches: &getopts::Matches)
583                 -> Option<(PpMode, Option<UserIdentifiedItem>)> {
584     let pretty = if sess.opts.debugging_opts.unstable_options {
585         matches.opt_default("pretty", "normal").map(|a| {
586             // stable pretty-print variants only
587             pretty::parse_pretty(sess, &a, false)
588         })
589     } else {
590         None
591     };
592
593     if pretty.is_none() {
594         sess.opts.debugging_opts.unpretty.as_ref().map(|a| {
595             // extended with unstable pretty-print variants
596             pretty::parse_pretty(sess, &a, true)
597         })
598     } else {
599         pretty
600     }
601 }
602
603 // Whether to stop or continue compilation.
604 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
605 pub enum Compilation {
606     Stop,
607     Continue,
608 }
609
610 impl Compilation {
611     pub fn and_then<F: FnOnce() -> Compilation>(self, next: F) -> Compilation {
612         match self {
613             Compilation::Stop => Compilation::Stop,
614             Compilation::Continue => next(),
615         }
616     }
617 }
618
619 // A trait for customising the compilation process. Offers a number of hooks for
620 // executing custom code or customising input.
621 pub trait CompilerCalls<'a> {
622     // Hook for a callback early in the process of handling arguments. This will
623     // be called straight after options have been parsed but before anything
624     // else (e.g., selecting input and output).
625     fn early_callback(&mut self,
626                       _: &getopts::Matches,
627                       _: &config::Options,
628                       _: &ast::CrateConfig,
629                       _: &errors::registry::Registry,
630                       _: ErrorOutputType)
631                       -> Compilation {
632         Compilation::Continue
633     }
634
635     // Hook for a callback late in the process of handling arguments. This will
636     // be called just before actual compilation starts (and before build_controller
637     // is called), after all arguments etc. have been completely handled.
638     fn late_callback(&mut self,
639                      _: &TransCrate,
640                      _: &getopts::Matches,
641                      _: &Session,
642                      _: &CrateStore,
643                      _: &Input,
644                      _: &Option<PathBuf>,
645                      _: &Option<PathBuf>)
646                      -> Compilation {
647         Compilation::Continue
648     }
649
650     // Called after we extract the input from the arguments. Gives the implementer
651     // an opportunity to change the inputs or to add some custom input handling.
652     // The default behaviour is to simply pass through the inputs.
653     fn some_input(&mut self,
654                   input: Input,
655                   input_path: Option<PathBuf>)
656                   -> (Input, Option<PathBuf>) {
657         (input, input_path)
658     }
659
660     // Called after we extract the input from the arguments if there is no valid
661     // input. Gives the implementer an opportunity to supply alternate input (by
662     // returning a Some value) or to add custom behaviour for this error such as
663     // emitting error messages. Returning None will cause compilation to stop
664     // at this point.
665     fn no_input(&mut self,
666                 _: &getopts::Matches,
667                 _: &config::Options,
668                 _: &ast::CrateConfig,
669                 _: &Option<PathBuf>,
670                 _: &Option<PathBuf>,
671                 _: &errors::registry::Registry)
672                 -> Option<(Input, Option<PathBuf>)> {
673         None
674     }
675
676     // Create a CompilController struct for controlling the behaviour of
677     // compilation.
678     fn build_controller(&mut self, _: &Session, _: &getopts::Matches) -> CompileController<'a>;
679 }
680
681 // CompilerCalls instance for a regular rustc build.
682 #[derive(Copy, Clone)]
683 pub struct RustcDefaultCalls;
684
685 // FIXME remove these and use winapi 0.3 instead
686 // Duplicates: bootstrap/compile.rs, librustc_errors/emitter.rs
687 #[cfg(unix)]
688 fn stdout_isatty() -> bool {
689     unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
690 }
691
692 #[cfg(windows)]
693 fn stdout_isatty() -> bool {
694     type DWORD = u32;
695     type BOOL = i32;
696     type HANDLE = *mut u8;
697     type LPDWORD = *mut u32;
698     const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
699     extern "system" {
700         fn GetStdHandle(which: DWORD) -> HANDLE;
701         fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL;
702     }
703     unsafe {
704         let handle = GetStdHandle(STD_OUTPUT_HANDLE);
705         let mut out = 0;
706         GetConsoleMode(handle, &mut out) != 0
707     }
708 }
709
710 fn handle_explain(code: &str,
711                   descriptions: &errors::registry::Registry,
712                   output: ErrorOutputType) {
713     let normalised = if code.starts_with("E") {
714         code.to_string()
715     } else {
716         format!("E{0:0>4}", code)
717     };
718     match descriptions.find_description(&normalised) {
719         Some(ref description) => {
720             let mut is_in_code_block = false;
721             let mut text = String::new();
722
723             // Slice off the leading newline and print.
724             for line in description[1..].lines() {
725                 let indent_level = line.find(|c: char| !c.is_whitespace())
726                     .unwrap_or_else(|| line.len());
727                 let dedented_line = &line[indent_level..];
728                 if dedented_line.starts_with("```") {
729                     is_in_code_block = !is_in_code_block;
730                     text.push_str(&line[..(indent_level+3)]);
731                 } else if is_in_code_block && dedented_line.starts_with("# ") {
732                     continue;
733                 } else {
734                     text.push_str(line);
735                 }
736                 text.push('\n');
737             }
738
739             if stdout_isatty() {
740                 show_content_with_pager(&text);
741             } else {
742                 print!("{}", text);
743             }
744         }
745         None => {
746             early_error(output, &format!("no extended information for {}", code));
747         }
748     }
749 }
750
751 fn show_content_with_pager(content: &String) {
752     let pager_name = env::var_os("PAGER").unwrap_or_else(|| if cfg!(windows) {
753         OsString::from("more.com")
754     } else {
755         OsString::from("less")
756     });
757
758     let mut fallback_to_println = false;
759
760     match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
761         Ok(mut pager) => {
762             if let Some(pipe) = pager.stdin.as_mut() {
763                 if pipe.write_all(content.as_bytes()).is_err() {
764                     fallback_to_println = true;
765                 }
766             }
767
768             if pager.wait().is_err() {
769                 fallback_to_println = true;
770             }
771         }
772         Err(_) => {
773             fallback_to_println = true;
774         }
775     }
776
777     // If pager fails for whatever reason, we should still print the content
778     // to standard output
779     if fallback_to_println {
780         print!("{}", content);
781     }
782 }
783
784 impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
785     fn early_callback(&mut self,
786                       matches: &getopts::Matches,
787                       _: &config::Options,
788                       _: &ast::CrateConfig,
789                       descriptions: &errors::registry::Registry,
790                       output: ErrorOutputType)
791                       -> Compilation {
792         if let Some(ref code) = matches.opt_str("explain") {
793             handle_explain(code, descriptions, output);
794             return Compilation::Stop;
795         }
796
797         Compilation::Continue
798     }
799
800     fn no_input(&mut self,
801                 matches: &getopts::Matches,
802                 sopts: &config::Options,
803                 cfg: &ast::CrateConfig,
804                 odir: &Option<PathBuf>,
805                 ofile: &Option<PathBuf>,
806                 descriptions: &errors::registry::Registry)
807                 -> Option<(Input, Option<PathBuf>)> {
808         match matches.free.len() {
809             0 => {
810                 let mut sess = build_session(sopts.clone(),
811                     None,
812                     descriptions.clone());
813                 if sopts.describe_lints {
814                     let mut ls = lint::LintStore::new();
815                     rustc_lint::register_builtins(&mut ls, Some(&sess));
816                     describe_lints(&sess, &ls, false);
817                     return None;
818                 }
819                 rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
820                 let mut cfg = config::build_configuration(&sess, cfg.clone());
821                 let trans = get_trans(&sess);
822                 target_features::add_configuration(&mut cfg, &sess, &*trans);
823                 sess.parse_sess.config = cfg;
824                 let should_stop = RustcDefaultCalls::print_crate_info(
825                     &*trans,
826                     &sess,
827                     None,
828                     odir,
829                     ofile
830                 );
831
832                 if should_stop == Compilation::Stop {
833                     return None;
834                 }
835                 early_error(sopts.error_format, "no input filename given");
836             }
837             1 => panic!("make_input should have provided valid inputs"),
838             _ => early_error(sopts.error_format, "multiple input filenames provided"),
839         }
840     }
841
842     fn late_callback(&mut self,
843                      trans: &TransCrate,
844                      matches: &getopts::Matches,
845                      sess: &Session,
846                      cstore: &CrateStore,
847                      input: &Input,
848                      odir: &Option<PathBuf>,
849                      ofile: &Option<PathBuf>)
850                      -> Compilation {
851         RustcDefaultCalls::print_crate_info(trans, sess, Some(input), odir, ofile)
852             .and_then(|| RustcDefaultCalls::list_metadata(sess, cstore, matches, input))
853     }
854
855     fn build_controller(&mut self,
856                         sess: &Session,
857                         matches: &getopts::Matches)
858                         -> CompileController<'a> {
859         let mut control = CompileController::basic();
860
861         control.keep_ast = sess.opts.debugging_opts.keep_ast;
862         control.continue_parse_after_error = sess.opts.debugging_opts.continue_parse_after_error;
863
864         if let Some((ppm, opt_uii)) = parse_pretty(sess, matches) {
865             if ppm.needs_ast_map(&opt_uii) {
866                 control.after_hir_lowering.stop = Compilation::Stop;
867
868                 control.after_parse.callback = box move |state| {
869                     state.krate = Some(pretty::fold_crate(state.session,
870                                                           state.krate.take().unwrap(),
871                                                           ppm));
872                 };
873                 control.after_hir_lowering.callback = box move |state| {
874                     pretty::print_after_hir_lowering(state.session,
875                                                      state.cstore.unwrap(),
876                                                      state.hir_map.unwrap(),
877                                                      state.analysis.unwrap(),
878                                                      state.resolutions.unwrap(),
879                                                      state.input,
880                                                      &state.expanded_crate.take().unwrap(),
881                                                      state.crate_name.unwrap(),
882                                                      ppm,
883                                                      state.arenas.unwrap(),
884                                                      state.output_filenames.unwrap(),
885                                                      opt_uii.clone(),
886                                                      state.out_file);
887                 };
888             } else {
889                 control.after_parse.stop = Compilation::Stop;
890
891                 control.after_parse.callback = box move |state| {
892                     let krate = pretty::fold_crate(state.session, state.krate.take().unwrap(), ppm);
893                     pretty::print_after_parsing(state.session,
894                                                 state.input,
895                                                 &krate,
896                                                 ppm,
897                                                 state.out_file);
898                 };
899             }
900
901             return control;
902         }
903
904         if sess.opts.debugging_opts.parse_only ||
905            sess.opts.debugging_opts.show_span.is_some() ||
906            sess.opts.debugging_opts.ast_json_noexpand {
907             control.after_parse.stop = Compilation::Stop;
908         }
909
910         if sess.opts.debugging_opts.no_analysis ||
911            sess.opts.debugging_opts.ast_json {
912             control.after_hir_lowering.stop = Compilation::Stop;
913         }
914
915         if sess.opts.debugging_opts.save_analysis {
916             enable_save_analysis(&mut control);
917         }
918
919         if sess.print_fuel_crate.is_some() {
920             let old_callback = control.compilation_done.callback;
921             control.compilation_done.callback = box move |state| {
922                 old_callback(state);
923                 let sess = state.session;
924                 println!("Fuel used by {}: {}",
925                     sess.print_fuel_crate.as_ref().unwrap(),
926                     sess.print_fuel.get());
927             }
928         }
929         control
930     }
931 }
932
933 pub fn enable_save_analysis(control: &mut CompileController) {
934     control.keep_ast = true;
935     control.after_analysis.callback = box |state| {
936         time(state.session, "save analysis", || {
937             save::process_crate(state.tcx.unwrap(),
938                                 state.expanded_crate.unwrap(),
939                                 state.analysis.unwrap(),
940                                 state.crate_name.unwrap(),
941                                 None,
942                                 DumpHandler::new(state.out_dir,
943                                                  state.crate_name.unwrap()))
944         });
945     };
946     control.after_analysis.run_callback_on_error = true;
947     control.make_glob_map = resolve::MakeGlobMap::Yes;
948 }
949
950 impl RustcDefaultCalls {
951     pub fn list_metadata(sess: &Session,
952                          cstore: &CrateStore,
953                          matches: &getopts::Matches,
954                          input: &Input)
955                          -> Compilation {
956         let r = matches.opt_strs("Z");
957         if r.contains(&("ls".to_string())) {
958             match input {
959                 &Input::File(ref ifile) => {
960                     let path = &(*ifile);
961                     let mut v = Vec::new();
962                     locator::list_file_metadata(&sess.target.target,
963                                                 path,
964                                                 cstore.metadata_loader(),
965                                                 &mut v)
966                             .unwrap();
967                     println!("{}", String::from_utf8(v).unwrap());
968                 }
969                 &Input::Str { .. } => {
970                     early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
971                 }
972             }
973             return Compilation::Stop;
974         }
975
976         return Compilation::Continue;
977     }
978
979
980     fn print_crate_info(trans: &TransCrate,
981                         sess: &Session,
982                         input: Option<&Input>,
983                         odir: &Option<PathBuf>,
984                         ofile: &Option<PathBuf>)
985                         -> Compilation {
986         use rustc::session::config::PrintRequest::*;
987         // PrintRequest::NativeStaticLibs is special - printed during linking
988         // (empty iterator returns true)
989         if sess.opts.prints.iter().all(|&p| p==PrintRequest::NativeStaticLibs) {
990             return Compilation::Continue;
991         }
992
993         let attrs = match input {
994             None => None,
995             Some(input) => {
996                 let result = parse_crate_attrs(sess, input);
997                 match result {
998                     Ok(attrs) => Some(attrs),
999                     Err(mut parse_error) => {
1000                         parse_error.emit();
1001                         return Compilation::Stop;
1002                     }
1003                 }
1004             }
1005         };
1006         for req in &sess.opts.prints {
1007             match *req {
1008                 TargetList => {
1009                     let mut targets = rustc_back::target::get_targets().collect::<Vec<String>>();
1010                     targets.sort();
1011                     println!("{}", targets.join("\n"));
1012                 },
1013                 Sysroot => println!("{}", sess.sysroot().display()),
1014                 TargetSpec => println!("{}", sess.target.target.to_json().pretty()),
1015                 FileNames | CrateName => {
1016                     let input = match input {
1017                         Some(input) => input,
1018                         None => early_error(ErrorOutputType::default(), "no input file provided"),
1019                     };
1020                     let attrs = attrs.as_ref().unwrap();
1021                     let t_outputs = driver::build_output_filenames(input, odir, ofile, attrs, sess);
1022                     let id = rustc_trans_utils::link::find_crate_name(Some(sess), attrs, input);
1023                     if *req == PrintRequest::CrateName {
1024                         println!("{}", id);
1025                         continue;
1026                     }
1027                     let crate_types = driver::collect_crate_types(sess, attrs);
1028                     for &style in &crate_types {
1029                         let fname = rustc_trans_utils::link::filename_for_input(
1030                             sess,
1031                             style,
1032                             &id,
1033                             &t_outputs
1034                         );
1035                         println!("{}",
1036                                  fname.file_name()
1037                                       .unwrap()
1038                                       .to_string_lossy());
1039                     }
1040                 }
1041                 Cfg => {
1042                     let allow_unstable_cfg = UnstableFeatures::from_environment()
1043                         .is_nightly_build();
1044
1045                     let mut cfgs = Vec::new();
1046                     for &(name, ref value) in sess.parse_sess.config.iter() {
1047                         let gated_cfg = GatedCfg::gate(&ast::MetaItem {
1048                             name,
1049                             node: ast::MetaItemKind::Word,
1050                             span: DUMMY_SP,
1051                         });
1052
1053                         // Note that crt-static is a specially recognized cfg
1054                         // directive that's printed out here as part of
1055                         // rust-lang/rust#37406, but in general the
1056                         // `target_feature` cfg is gated under
1057                         // rust-lang/rust#29717. For now this is just
1058                         // specifically allowing the crt-static cfg and that's
1059                         // it, this is intended to get into Cargo and then go
1060                         // through to build scripts.
1061                         let value = value.as_ref().map(|s| s.as_str());
1062                         let value = value.as_ref().map(|s| s.as_ref());
1063                         if name != "target_feature" || value != Some("crt-static") {
1064                             if !allow_unstable_cfg && gated_cfg.is_some() {
1065                                 continue;
1066                             }
1067                         }
1068
1069                         cfgs.push(if let Some(value) = value {
1070                             format!("{}=\"{}\"", name, value)
1071                         } else {
1072                             format!("{}", name)
1073                         });
1074                     }
1075
1076                     cfgs.sort();
1077                     for cfg in cfgs {
1078                         println!("{}", cfg);
1079                     }
1080                 }
1081                 RelocationModels | CodeModels | TlsModels | TargetCPUs | TargetFeatures => {
1082                     trans.print(*req, sess);
1083                 }
1084                 // Any output here interferes with Cargo's parsing of other printed output
1085                 PrintRequest::NativeStaticLibs => {}
1086             }
1087         }
1088         return Compilation::Stop;
1089     }
1090 }
1091
1092 /// Returns a version string such as "0.12.0-dev".
1093 fn release_str() -> Option<&'static str> {
1094     option_env!("CFG_RELEASE")
1095 }
1096
1097 /// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built.
1098 fn commit_hash_str() -> Option<&'static str> {
1099     option_env!("CFG_VER_HASH")
1100 }
1101
1102 /// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string.
1103 fn commit_date_str() -> Option<&'static str> {
1104     option_env!("CFG_VER_DATE")
1105 }
1106
1107 /// Prints version information
1108 pub fn version(binary: &str, matches: &getopts::Matches) {
1109     let verbose = matches.opt_present("verbose");
1110
1111     println!("{} {}",
1112              binary,
1113              option_env!("CFG_VERSION").unwrap_or("unknown version"));
1114     if verbose {
1115         fn unw(x: Option<&str>) -> &str {
1116             x.unwrap_or("unknown")
1117         }
1118         println!("binary: {}", binary);
1119         println!("commit-hash: {}", unw(commit_hash_str()));
1120         println!("commit-date: {}", unw(commit_date_str()));
1121         println!("host: {}", config::host_triple());
1122         println!("release: {}", unw(release_str()));
1123         get_trans_sysroot("llvm")().print_version();
1124     }
1125 }
1126
1127 fn usage(verbose: bool, include_unstable_options: bool) {
1128     let groups = if verbose {
1129         config::rustc_optgroups()
1130     } else {
1131         config::rustc_short_optgroups()
1132     };
1133     let mut options = getopts::Options::new();
1134     for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) {
1135         (option.apply)(&mut options);
1136     }
1137     let message = format!("Usage: rustc [OPTIONS] INPUT");
1138     let nightly_help = if nightly_options::is_nightly_build() {
1139         "\n    -Z help             Print internal options for debugging rustc"
1140     } else {
1141         ""
1142     };
1143     let verbose_help = if verbose {
1144         ""
1145     } else {
1146         "\n    --help -v           Print the full set of options rustc accepts"
1147     };
1148     println!("{}\nAdditional help:
1149     -C help             Print codegen options
1150     -W help             \
1151               Print 'lint' options and default settings{}{}\n",
1152              options.usage(&message),
1153              nightly_help,
1154              verbose_help);
1155 }
1156
1157 fn print_wall_help() {
1158     println!("
1159 The flag `-Wall` does not exist in `rustc`. Most useful lints are enabled by
1160 default. Use `rustc -W help` to see all available lints. It's more common to put
1161 warning settings in the crate root using `#![warn(LINT_NAME)]` instead of using
1162 the command line flag directly.
1163 ");
1164 }
1165
1166 fn describe_lints(sess: &Session, lint_store: &lint::LintStore, loaded_plugins: bool) {
1167     println!("
1168 Available lint options:
1169     -W <foo>           Warn about <foo>
1170     -A <foo>           \
1171               Allow <foo>
1172     -D <foo>           Deny <foo>
1173     -F <foo>           Forbid <foo> \
1174               (deny <foo> and all attempts to override)
1175
1176 ");
1177
1178     fn sort_lints(sess: &Session, lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> {
1179         let mut lints: Vec<_> = lints.into_iter().map(|(x, _)| x).collect();
1180         lints.sort_by(|x: &&Lint, y: &&Lint| {
1181             match x.default_level(sess).cmp(&y.default_level(sess)) {
1182                 // The sort doesn't case-fold but it's doubtful we care.
1183                 Equal => x.name.cmp(y.name),
1184                 r => r,
1185             }
1186         });
1187         lints
1188     }
1189
1190     fn sort_lint_groups(lints: Vec<(&'static str, Vec<lint::LintId>, bool)>)
1191                         -> Vec<(&'static str, Vec<lint::LintId>)> {
1192         let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect();
1193         lints.sort_by(|&(x, _): &(&'static str, Vec<lint::LintId>),
1194                        &(y, _): &(&'static str, Vec<lint::LintId>)| {
1195             x.cmp(y)
1196         });
1197         lints
1198     }
1199
1200     let (plugin, builtin): (Vec<_>, _) = lint_store.get_lints()
1201                                                    .iter()
1202                                                    .cloned()
1203                                                    .partition(|&(_, p)| p);
1204     let plugin = sort_lints(sess, plugin);
1205     let builtin = sort_lints(sess, builtin);
1206
1207     let (plugin_groups, builtin_groups): (Vec<_>, _) = lint_store.get_lint_groups()
1208                                                                  .iter()
1209                                                                  .cloned()
1210                                                                  .partition(|&(.., p)| p);
1211     let plugin_groups = sort_lint_groups(plugin_groups);
1212     let builtin_groups = sort_lint_groups(builtin_groups);
1213
1214     let max_name_len = plugin.iter()
1215                              .chain(&builtin)
1216                              .map(|&s| s.name.chars().count())
1217                              .max()
1218                              .unwrap_or(0);
1219     let padded = |x: &str| {
1220         let mut s = repeat(" ")
1221                         .take(max_name_len - x.chars().count())
1222                         .collect::<String>();
1223         s.push_str(x);
1224         s
1225     };
1226
1227     println!("Lint checks provided by rustc:\n");
1228     println!("    {}  {:7.7}  {}", padded("name"), "default", "meaning");
1229     println!("    {}  {:7.7}  {}", padded("----"), "-------", "-------");
1230
1231     let print_lints = |lints: Vec<&Lint>| {
1232         for lint in lints {
1233             let name = lint.name_lower().replace("_", "-");
1234             println!("    {}  {:7.7}  {}",
1235                      padded(&name),
1236                      lint.default_level.as_str(),
1237                      lint.desc);
1238         }
1239         println!("\n");
1240     };
1241
1242     print_lints(builtin);
1243
1244
1245
1246     let max_name_len = max("warnings".len(),
1247                            plugin_groups.iter()
1248                                         .chain(&builtin_groups)
1249                                         .map(|&(s, _)| s.chars().count())
1250                                         .max()
1251                                         .unwrap_or(0));
1252
1253     let padded = |x: &str| {
1254         let mut s = repeat(" ")
1255                         .take(max_name_len - x.chars().count())
1256                         .collect::<String>();
1257         s.push_str(x);
1258         s
1259     };
1260
1261     println!("Lint groups provided by rustc:\n");
1262     println!("    {}  {}", padded("name"), "sub-lints");
1263     println!("    {}  {}", padded("----"), "---------");
1264     println!("    {}  {}", padded("warnings"), "all lints that are set to issue warnings");
1265
1266     let print_lint_groups = |lints: Vec<(&'static str, Vec<lint::LintId>)>| {
1267         for (name, to) in lints {
1268             let name = name.to_lowercase().replace("_", "-");
1269             let desc = to.into_iter()
1270                          .map(|x| x.to_string().replace("_", "-"))
1271                          .collect::<Vec<String>>()
1272                          .join(", ");
1273             println!("    {}  {}", padded(&name), desc);
1274         }
1275         println!("\n");
1276     };
1277
1278     print_lint_groups(builtin_groups);
1279
1280     match (loaded_plugins, plugin.len(), plugin_groups.len()) {
1281         (false, 0, _) | (false, _, 0) => {
1282             println!("Compiler plugins can provide additional lints and lint groups. To see a \
1283                       listing of these, re-run `rustc -W help` with a crate filename.");
1284         }
1285         (false, ..) => panic!("didn't load lint plugins but got them anyway!"),
1286         (true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."),
1287         (true, l, g) => {
1288             if l > 0 {
1289                 println!("Lint checks provided by plugins loaded by this crate:\n");
1290                 print_lints(plugin);
1291             }
1292             if g > 0 {
1293                 println!("Lint groups provided by plugins loaded by this crate:\n");
1294                 print_lint_groups(plugin_groups);
1295             }
1296         }
1297     }
1298 }
1299
1300 fn describe_debug_flags() {
1301     println!("\nAvailable debug options:\n");
1302     print_flag_list("-Z", config::DB_OPTIONS);
1303 }
1304
1305 fn describe_codegen_flags() {
1306     println!("\nAvailable codegen options:\n");
1307     print_flag_list("-C", config::CG_OPTIONS);
1308 }
1309
1310 fn print_flag_list<T>(cmdline_opt: &str,
1311                       flag_list: &[(&'static str, T, Option<&'static str>, &'static str)]) {
1312     let max_len = flag_list.iter()
1313                            .map(|&(name, _, opt_type_desc, _)| {
1314                                let extra_len = match opt_type_desc {
1315                                    Some(..) => 4,
1316                                    None => 0,
1317                                };
1318                                name.chars().count() + extra_len
1319                            })
1320                            .max()
1321                            .unwrap_or(0);
1322
1323     for &(name, _, opt_type_desc, desc) in flag_list {
1324         let (width, extra) = match opt_type_desc {
1325             Some(..) => (max_len - 4, "=val"),
1326             None => (max_len, ""),
1327         };
1328         println!("    {} {:>width$}{} -- {}",
1329                  cmdline_opt,
1330                  name.replace("_", "-"),
1331                  extra,
1332                  desc,
1333                  width = width);
1334     }
1335 }
1336
1337 /// Process command line options. Emits messages as appropriate. If compilation
1338 /// should continue, returns a getopts::Matches object parsed from args,
1339 /// otherwise returns None.
1340 ///
1341 /// The compiler's handling of options is a little complicated as it ties into
1342 /// our stability story, and it's even *more* complicated by historical
1343 /// accidents. The current intention of each compiler option is to have one of
1344 /// three modes:
1345 ///
1346 /// 1. An option is stable and can be used everywhere.
1347 /// 2. An option is unstable, but was historically allowed on the stable
1348 ///    channel.
1349 /// 3. An option is unstable, and can only be used on nightly.
1350 ///
1351 /// Like unstable library and language features, however, unstable options have
1352 /// always required a form of "opt in" to indicate that you're using them. This
1353 /// provides the easy ability to scan a code base to check to see if anything
1354 /// unstable is being used. Currently, this "opt in" is the `-Z` "zed" flag.
1355 ///
1356 /// All options behind `-Z` are considered unstable by default. Other top-level
1357 /// options can also be considered unstable, and they were unlocked through the
1358 /// `-Z unstable-options` flag. Note that `-Z` remains to be the root of
1359 /// instability in both cases, though.
1360 ///
1361 /// So with all that in mind, the comments below have some more detail about the
1362 /// contortions done here to get things to work out correctly.
1363 pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
1364     // Throw away the first argument, the name of the binary
1365     let args = &args[1..];
1366
1367     if args.is_empty() {
1368         // user did not write `-v` nor `-Z unstable-options`, so do not
1369         // include that extra information.
1370         usage(false, false);
1371         return None;
1372     }
1373
1374     // Parse with *all* options defined in the compiler, we don't worry about
1375     // option stability here we just want to parse as much as possible.
1376     let mut options = getopts::Options::new();
1377     for option in config::rustc_optgroups() {
1378         (option.apply)(&mut options);
1379     }
1380     let matches = match options.parse(args) {
1381         Ok(m) => m,
1382         Err(f) => early_error(ErrorOutputType::default(), &f.to_string()),
1383     };
1384
1385     // For all options we just parsed, we check a few aspects:
1386     //
1387     // * If the option is stable, we're all good
1388     // * If the option wasn't passed, we're all good
1389     // * If `-Z unstable-options` wasn't passed (and we're not a -Z option
1390     //   ourselves), then we require the `-Z unstable-options` flag to unlock
1391     //   this option that was passed.
1392     // * If we're a nightly compiler, then unstable options are now unlocked, so
1393     //   we're good to go.
1394     // * Otherwise, if we're a truly unstable option then we generate an error
1395     //   (unstable option being used on stable)
1396     // * If we're a historically stable-but-should-be-unstable option then we
1397     //   emit a warning that we're going to turn this into an error soon.
1398     nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
1399
1400     if matches.opt_present("h") || matches.opt_present("help") {
1401         // Only show unstable options in --help if we *really* accept unstable
1402         // options, which catches the case where we got `-Z unstable-options` on
1403         // the stable channel of Rust which was accidentally allowed
1404         // historically.
1405         usage(matches.opt_present("verbose"),
1406               nightly_options::is_unstable_enabled(&matches));
1407         return None;
1408     }
1409
1410     // Handle the special case of -Wall.
1411     let wall = matches.opt_strs("W");
1412     if wall.iter().any(|x| *x == "all") {
1413         print_wall_help();
1414         return None;
1415     }
1416
1417     // Don't handle -W help here, because we might first load plugins.
1418     let r = matches.opt_strs("Z");
1419     if r.iter().any(|x| *x == "help") {
1420         describe_debug_flags();
1421         return None;
1422     }
1423
1424     let cg_flags = matches.opt_strs("C");
1425     if cg_flags.iter().any(|x| *x == "help") {
1426         describe_codegen_flags();
1427         return None;
1428     }
1429
1430     if cg_flags.iter().any(|x| *x == "no-stack-check") {
1431         early_warn(ErrorOutputType::default(),
1432                    "the --no-stack-check flag is deprecated and does nothing");
1433     }
1434
1435     if cg_flags.contains(&"passes=list".to_string()) {
1436         get_trans_sysroot("llvm")().print_passes();
1437         return None;
1438     }
1439
1440     if matches.opt_present("version") {
1441         version("rustc", &matches);
1442         return None;
1443     }
1444
1445     Some(matches)
1446 }
1447
1448 fn parse_crate_attrs<'a>(sess: &'a Session, input: &Input) -> PResult<'a, Vec<ast::Attribute>> {
1449     match *input {
1450         Input::File(ref ifile) => {
1451             parse::parse_crate_attrs_from_file(ifile, &sess.parse_sess)
1452         }
1453         Input::Str { ref name, ref input } => {
1454             parse::parse_crate_attrs_from_source_str(name.clone(),
1455                                                      input.clone(),
1456                                                      &sess.parse_sess)
1457         }
1458     }
1459 }
1460
1461 /// Runs `f` in a suitable thread for running `rustc`; returns a
1462 /// `Result` with either the return value of `f` or -- if a panic
1463 /// occurs -- the panic value.
1464 pub fn in_rustc_thread<F, R>(f: F) -> Result<R, Box<Any + Send>>
1465     where F: FnOnce() -> R + Send + 'static,
1466           R: Send + 'static,
1467 {
1468     // Temporarily have stack size set to 16MB to deal with nom-using crates failing
1469     const STACK_SIZE: usize = 16 * 1024 * 1024; // 16MB
1470
1471     #[cfg(unix)]
1472     let spawn_thread = unsafe {
1473         // Fetch the current resource limits
1474         let mut rlim = libc::rlimit {
1475             rlim_cur: 0,
1476             rlim_max: 0,
1477         };
1478         if libc::getrlimit(libc::RLIMIT_STACK, &mut rlim) != 0 {
1479             let err = io::Error::last_os_error();
1480             error!("in_rustc_thread: error calling getrlimit: {}", err);
1481             true
1482         } else if rlim.rlim_max < STACK_SIZE as libc::rlim_t {
1483             true
1484         } else {
1485             std::rt::deinit_stack_guard();
1486             rlim.rlim_cur = STACK_SIZE as libc::rlim_t;
1487             if libc::setrlimit(libc::RLIMIT_STACK, &mut rlim) != 0 {
1488                 let err = io::Error::last_os_error();
1489                 error!("in_rustc_thread: error calling setrlimit: {}", err);
1490                 std::rt::update_stack_guard();
1491                 true
1492             } else {
1493                 std::rt::update_stack_guard();
1494                 false
1495             }
1496         }
1497     };
1498
1499     // We set the stack size at link time. See src/rustc/rustc.rs.
1500     #[cfg(windows)]
1501     let spawn_thread = false;
1502
1503     #[cfg(not(any(windows,unix)))]
1504     let spawn_thread = true;
1505
1506     // The or condition is added from backward compatibility.
1507     if spawn_thread || env::var_os("RUST_MIN_STACK").is_some() {
1508         let mut cfg = thread::Builder::new().name("rustc".to_string());
1509
1510         // FIXME: Hacks on hacks. If the env is trying to override the stack size
1511         // then *don't* set it explicitly.
1512         if env::var_os("RUST_MIN_STACK").is_none() {
1513             cfg = cfg.stack_size(STACK_SIZE);
1514         }
1515
1516         let thread = cfg.spawn(f);
1517         thread.unwrap().join()
1518     } else {
1519         Ok(f())
1520     }
1521 }
1522
1523 /// Get a list of extra command-line flags provided by the user, as strings.
1524 ///
1525 /// This function is used during ICEs to show more information useful for
1526 /// debugging, since some ICEs only happens with non-default compiler flags
1527 /// (and the users don't always report them).
1528 fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
1529     let mut args = Vec::new();
1530     for arg in env::args_os() {
1531         args.push(arg.to_string_lossy().to_string());
1532     }
1533
1534     // Avoid printing help because of empty args. This can suggest the compiler
1535     // itself is not the program root (consider RLS).
1536     if args.len() < 2 {
1537         return None;
1538     }
1539
1540     let matches = if let Some(matches) = handle_options(&args) {
1541         matches
1542     } else {
1543         return None;
1544     };
1545
1546     let mut result = Vec::new();
1547     let mut excluded_cargo_defaults = false;
1548     for flag in ICE_REPORT_COMPILER_FLAGS {
1549         let prefix = if flag.len() == 1 { "-" } else { "--" };
1550
1551         for content in &matches.opt_strs(flag) {
1552             // Split always returns the first element
1553             let name = if let Some(first) = content.split('=').next() {
1554                 first
1555             } else {
1556                 &content
1557             };
1558
1559             let content = if ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.contains(&name) {
1560                 name
1561             } else {
1562                 content
1563             };
1564
1565             if !ICE_REPORT_COMPILER_FLAGS_EXCLUDE.contains(&name) {
1566                 result.push(format!("{}{} {}", prefix, flag, content));
1567             } else {
1568                 excluded_cargo_defaults = true;
1569             }
1570         }
1571     }
1572
1573     if result.len() > 0 {
1574         Some((result, excluded_cargo_defaults))
1575     } else {
1576         None
1577     }
1578 }
1579
1580 /// Run a procedure which will detect panics in the compiler and print nicer
1581 /// error messages rather than just failing the test.
1582 ///
1583 /// The diagnostic emitter yielded to the procedure should be used for reporting
1584 /// errors of the compiler.
1585 pub fn monitor<F: FnOnce() + Send + 'static>(f: F) {
1586     let result = in_rustc_thread(move || {
1587         f()
1588     });
1589
1590     if let Err(value) = result {
1591         // Thread panicked without emitting a fatal diagnostic
1592         if !value.is::<errors::FatalErrorMarker>() {
1593             // Emit a newline
1594             eprintln!("");
1595
1596             let emitter =
1597                 Box::new(errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto,
1598                                                                 None,
1599                                                                 false,
1600                                                                 false));
1601             let handler = errors::Handler::with_emitter(true, false, emitter);
1602
1603             // a .span_bug or .bug call has already printed what
1604             // it wants to print.
1605             if !value.is::<errors::ExplicitBug>() {
1606                 handler.emit(&MultiSpan::new(),
1607                              "unexpected panic",
1608                              errors::Level::Bug);
1609             }
1610
1611             let mut xs = vec![
1612                 "the compiler unexpectedly panicked. this is a bug.".to_string(),
1613                 format!("we would appreciate a bug report: {}", BUG_REPORT_URL),
1614                 format!("rustc {} running on {}",
1615                         option_env!("CFG_VERSION").unwrap_or("unknown_version"),
1616                         config::host_triple()),
1617             ];
1618
1619             if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
1620                 xs.push(format!("compiler flags: {}", flags.join(" ")));
1621
1622                 if excluded_cargo_defaults {
1623                     xs.push("some of the compiler flags provided by cargo are hidden".to_string());
1624                 }
1625             }
1626
1627             for note in &xs {
1628                 handler.emit(&MultiSpan::new(),
1629                              &note,
1630                              errors::Level::Note);
1631             }
1632         }
1633
1634         panic::resume_unwind(Box::new(errors::FatalErrorMarker));
1635     }
1636 }
1637
1638 pub fn diagnostics_registry() -> errors::registry::Registry {
1639     use errors::registry::Registry;
1640
1641     let mut all_errors = Vec::new();
1642     all_errors.extend_from_slice(&rustc::DIAGNOSTICS);
1643     all_errors.extend_from_slice(&rustc_typeck::DIAGNOSTICS);
1644     all_errors.extend_from_slice(&rustc_resolve::DIAGNOSTICS);
1645     all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS);
1646     // FIXME: need to figure out a way to get these back in here
1647     // all_errors.extend_from_slice(get_trans(sess).diagnostics());
1648     all_errors.extend_from_slice(&rustc_trans_utils::DIAGNOSTICS);
1649     all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS);
1650     all_errors.extend_from_slice(&rustc_passes::DIAGNOSTICS);
1651     all_errors.extend_from_slice(&rustc_plugin::DIAGNOSTICS);
1652     all_errors.extend_from_slice(&rustc_mir::DIAGNOSTICS);
1653     all_errors.extend_from_slice(&syntax::DIAGNOSTICS);
1654
1655     Registry::new(&all_errors)
1656 }
1657
1658 /// This allows tools to enable rust logging without having to magically match rustc's
1659 /// log crate version
1660 pub fn init_rustc_env_logger() {
1661     env_logger::init();
1662 }
1663
1664 pub fn main() {
1665     init_rustc_env_logger();
1666     let result = run(|| {
1667         let args = env::args_os().enumerate()
1668             .map(|(i, arg)| arg.into_string().unwrap_or_else(|arg| {
1669                 early_error(ErrorOutputType::default(),
1670                             &format!("Argument {} is not valid Unicode: {:?}", i, arg))
1671             }))
1672             .collect::<Vec<_>>();
1673         run_compiler(&args,
1674                      &mut RustcDefaultCalls,
1675                      None,
1676                      None)
1677     });
1678     process::exit(result as i32);
1679 }