]> git.lizzy.rs Git - rust.git/blob - src/driver.rs
deps: bump toml from 0.4 to 0.5
[rust.git] / src / driver.rs
1 #![feature(rustc_private)]
2
3 // FIXME: switch to something more ergonomic here, once available.
4 // (Currently there is no way to opt into sysroot crates without `extern crate`.)
5 #[allow(unused_extern_crates)]
6 extern crate rustc_driver;
7 #[allow(unused_extern_crates)]
8 extern crate rustc_interface;
9 #[allow(unused_extern_crates)]
10 extern crate rustc_plugin;
11
12 use rustc_interface::interface;
13 use std::path::Path;
14 use std::process::{exit, Command};
15
16 fn show_version() {
17     println!(env!("CARGO_PKG_VERSION"));
18 }
19
20 /// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
21 /// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
22 fn arg_value<'a>(
23     args: impl IntoIterator<Item = &'a String>,
24     find_arg: &str,
25     pred: impl Fn(&str) -> bool,
26 ) -> Option<&'a str> {
27     let mut args = args.into_iter().map(String::as_str);
28
29     while let Some(arg) = args.next() {
30         let arg: Vec<_> = arg.splitn(2, '=').collect();
31         if arg.get(0) != Some(&find_arg) {
32             continue;
33         }
34
35         let value = arg.get(1).cloned().or_else(|| args.next());
36         if value.as_ref().map_or(false, |p| pred(p)) {
37             return value;
38         }
39     }
40     None
41 }
42
43 #[test]
44 fn test_arg_value() {
45     let args: Vec<_> = ["--bar=bar", "--foobar", "123", "--foo"]
46         .iter()
47         .map(std::string::ToString::to_string)
48         .collect();
49
50     assert_eq!(arg_value(None, "--foobar", |_| true), None);
51     assert_eq!(arg_value(&args, "--bar", |_| false), None);
52     assert_eq!(arg_value(&args, "--bar", |_| true), Some("bar"));
53     assert_eq!(arg_value(&args, "--bar", |p| p == "bar"), Some("bar"));
54     assert_eq!(arg_value(&args, "--bar", |p| p == "foo"), None);
55     assert_eq!(arg_value(&args, "--foobar", |p| p == "foo"), None);
56     assert_eq!(arg_value(&args, "--foobar", |p| p == "123"), Some("123"));
57     assert_eq!(arg_value(&args, "--foo", |_| true), None);
58 }
59
60 #[allow(clippy::too_many_lines)]
61
62 struct ClippyCallbacks;
63
64 impl rustc_driver::Callbacks for ClippyCallbacks {
65     fn after_parsing(&mut self, compiler: &interface::Compiler) -> bool {
66         let sess = compiler.session();
67         let mut registry = rustc_plugin::registry::Registry::new(
68             sess,
69             compiler
70                 .parse()
71                 .expect(
72                     "at this compilation stage \
73                      the crate must be parsed",
74                 )
75                 .peek()
76                 .span,
77         );
78         registry.args_hidden = Some(Vec::new());
79
80         let conf = clippy_lints::read_conf(&registry);
81         clippy_lints::register_plugins(&mut registry, &conf);
82
83         let rustc_plugin::registry::Registry {
84             early_lint_passes,
85             late_lint_passes,
86             lint_groups,
87             llvm_passes,
88             attributes,
89             ..
90         } = registry;
91         let mut ls = sess.lint_store.borrow_mut();
92         for pass in early_lint_passes {
93             ls.register_early_pass(Some(sess), true, false, pass);
94         }
95         for pass in late_lint_passes {
96             ls.register_late_pass(Some(sess), true, false, false, pass);
97         }
98
99         for (name, (to, deprecated_name)) in lint_groups {
100             ls.register_group(Some(sess), true, name, deprecated_name, to);
101         }
102         clippy_lints::register_pre_expansion_lints(sess, &mut ls, &conf);
103         clippy_lints::register_renamed(&mut ls);
104
105         sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
106         sess.plugin_attributes.borrow_mut().extend(attributes);
107
108         // Continue execution
109         true
110     }
111 }
112
113 pub fn main() {
114     rustc_driver::init_rustc_env_logger();
115     exit(
116         rustc_driver::report_ices_to_stderr_if_any(move || {
117             use std::env;
118
119             if std::env::args().any(|a| a == "--version" || a == "-V") {
120                 show_version();
121                 exit(0);
122             }
123
124             let mut orig_args: Vec<String> = env::args().collect();
125
126             // Get the sysroot, looking from most specific to this invocation to the least:
127             // - command line
128             // - runtime environment
129             //    - SYSROOT
130             //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
131             // - sysroot from rustc in the path
132             // - compile-time environment
133             let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
134             let have_sys_root_arg = sys_root_arg.is_some();
135             let sys_root = sys_root_arg
136                 .map(std::string::ToString::to_string)
137                 .or_else(|| std::env::var("SYSROOT").ok())
138                 .or_else(|| {
139                     let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
140                     let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
141                     home.and_then(|home| toolchain.map(|toolchain| format!("{}/toolchains/{}", home, toolchain)))
142                 })
143                 .or_else(|| {
144                     Command::new("rustc")
145                         .arg("--print")
146                         .arg("sysroot")
147                         .output()
148                         .ok()
149                         .and_then(|out| String::from_utf8(out.stdout).ok())
150                         .map(|s| s.trim().to_owned())
151                 })
152                 .or_else(|| option_env!("SYSROOT").map(String::from))
153                 .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");
154
155             // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
156             // We're invoking the compiler programmatically, so we ignore this/
157             if orig_args.len() <= 1 {
158                 std::process::exit(1);
159             }
160             if Path::new(&orig_args[1]).file_stem() == Some("rustc".as_ref()) {
161                 // we still want to be able to invoke it normally though
162                 orig_args.remove(1);
163             }
164             // this conditional check for the --sysroot flag is there so users can call
165             // `clippy_driver` directly
166             // without having to pass --sysroot or anything
167             let mut args: Vec<String> = if have_sys_root_arg {
168                 orig_args.clone()
169             } else {
170                 orig_args
171                     .clone()
172                     .into_iter()
173                     .chain(Some("--sysroot".to_owned()))
174                     .chain(Some(sys_root))
175                     .collect()
176             };
177
178             // this check ensures that dependencies are built but not linted and the final
179             // crate is
180             // linted but not built
181             let clippy_enabled = env::var("CLIPPY_TESTS").ok().map_or(false, |val| val == "true")
182                 || arg_value(&orig_args, "--emit", |val| val.split(',').any(|e| e == "metadata")).is_some();
183
184             if clippy_enabled {
185                 args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
186                 if let Ok(extra_args) = env::var("CLIPPY_ARGS") {
187                     args.extend(extra_args.split("__CLIPPY_HACKERY__").filter_map(|s| {
188                         if s.is_empty() {
189                             None
190                         } else {
191                             Some(s.to_string())
192                         }
193                     }));
194                 }
195             }
196
197             let mut clippy = ClippyCallbacks;
198             let mut default = rustc_driver::DefaultCallbacks;
199             let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
200                 if clippy_enabled { &mut clippy } else { &mut default };
201             let args = args;
202             rustc_driver::run_compiler(&args, callbacks, None, None)
203         })
204         .and_then(|result| result)
205         .is_err() as i32,
206     )
207 }