Skip to content

Commit 085967b

Browse files
committed
Update rust-semverver driver
Instead of having a custom `CompilerCalls` impl, the analysis pass is just injected into the default compiler.
1 parent 5c20a31 commit 085967b

File tree

1 file changed

+119
-205
lines changed

1 file changed

+119
-205
lines changed

src/bin/rust_semverver.rs

+119-205
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(box_syntax)]
22
#![feature(rustc_private)]
3+
#![feature(try_from)]
34

45
extern crate env_logger;
56
extern crate getopts;
@@ -13,223 +14,136 @@ extern crate rustc_metadata;
1314
extern crate semverver;
1415
extern crate syntax;
1516

16-
use rustc::{
17-
hir::def_id::*,
18-
middle::cstore::ExternCrate,
19-
session::{
20-
config::{self, ErrorOutputType, Input},
21-
Session,
22-
},
23-
};
24-
use rustc_codegen_utils::codegen_backend::CodegenBackend;
25-
use rustc_driver::{driver, Compilation, CompilerCalls, RustcDefaultCalls};
26-
use rustc_metadata::cstore::CStore;
17+
use rustc::{hir::def_id::*, middle::cstore::ExternCrate};
18+
use rustc_driver::{driver::CompileController, Compilation};
2719
use semverver::semcheck::run_analysis;
20+
use std::convert::TryInto;
2821
use std::{
29-
path::{Path, PathBuf},
30-
process::Command,
22+
path::Path,
23+
process::{exit, Command},
3124
};
32-
use syntax::{ast, source_map::Pos};
33-
34-
/// After the typechecker has finished it's work, perform our checks.
35-
fn callback(state: &driver::CompileState, version: &str, verbose: bool, api_guidelines: bool) {
36-
let tcx = state.tcx.unwrap();
37-
38-
// To select the old and new crates we look at the position of the declaration in the
39-
// source file. The first one will be the `old` and the other will be `new`. This is
40-
// unfortunately a bit hacky... See issue #64 for details.
41-
42-
let mut crates: Vec<_> = tcx
43-
.crates()
44-
.iter()
45-
.flat_map(|crate_num| {
46-
let def_id = DefId {
47-
krate: *crate_num,
48-
index: CRATE_DEF_INDEX,
49-
};
50-
51-
match *tcx.extern_crate(def_id) {
52-
Some(ExternCrate {
53-
span, direct: true, ..
54-
}) if span.data().lo.to_usize() > 0 => Some((span.data().lo.to_usize(), def_id)),
55-
_ => None,
56-
}
57-
})
58-
.collect();
59-
60-
crates.sort_by_key(|&(span_lo, _)| span_lo);
61-
62-
if let [(_, old_def_id), (_, new_def_id)] = *crates.as_slice() {
63-
debug!("running semver analysis");
64-
let changes = run_analysis(tcx, old_def_id, new_def_id);
65-
changes.output(tcx.sess, version, verbose, api_guidelines);
66-
} else {
67-
tcx.sess.err("could not find crate old and new crates");
68-
}
69-
}
25+
use syntax::source_map::Pos;
7026

71-
/// A wrapper to control compilation.
72-
struct SemVerVerCompilerCalls {
73-
/// The wrapped compilation handle.
74-
default: Box<RustcDefaultCalls>,
75-
/// The version of the old crate.
76-
version: String,
77-
/// The output mode.
78-
verbose: bool,
79-
/// Output only changes that are breaking according to the API guidelines.
80-
api_guidelines: bool,
81-
}
82-
83-
impl SemVerVerCompilerCalls {
84-
/// Construct a new compilation wrapper, given a version string.
85-
pub fn new(version: String, verbose: bool, api_guidelines: bool) -> Box<Self> {
86-
Box::new(Self {
87-
default: Box::new(RustcDefaultCalls),
88-
version,
89-
verbose,
90-
api_guidelines,
91-
})
92-
}
93-
94-
pub fn get_default(&self) -> Box<RustcDefaultCalls> {
95-
self.default.clone()
96-
}
97-
98-
pub fn get_version(&self) -> &String {
99-
&self.version
100-
}
101-
102-
pub fn get_api_guidelines(&self) -> bool {
103-
self.api_guidelines
104-
}
105-
}
106-
107-
impl<'a> CompilerCalls<'a> for SemVerVerCompilerCalls {
108-
fn early_callback(
109-
&mut self,
110-
matches: &getopts::Matches,
111-
sopts: &config::Options,
112-
cfg: &ast::CrateConfig,
113-
descriptions: &rustc_errors::registry::Registry,
114-
output: ErrorOutputType,
115-
) -> Compilation {
116-
debug!("running rust-semverver early_callback");
117-
self.default
118-
.early_callback(matches, sopts, cfg, descriptions, output)
119-
}
120-
121-
fn no_input(
122-
&mut self,
123-
matches: &getopts::Matches,
124-
sopts: &config::Options,
125-
cfg: &ast::CrateConfig,
126-
odir: &Option<PathBuf>,
127-
ofile: &Option<PathBuf>,
128-
descriptions: &rustc_errors::registry::Registry,
129-
) -> Option<(Input, Option<PathBuf>)> {
130-
debug!("running rust-semverver no_input");
131-
self.default
132-
.no_input(matches, sopts, cfg, odir, ofile, descriptions)
133-
}
134-
135-
fn late_callback(
136-
&mut self,
137-
trans_crate: &CodegenBackend,
138-
matches: &getopts::Matches,
139-
sess: &Session,
140-
cstore: &CStore,
141-
input: &Input,
142-
odir: &Option<PathBuf>,
143-
ofile: &Option<PathBuf>,
144-
) -> Compilation {
145-
debug!("running rust-semverver late_callback");
146-
self.default
147-
.late_callback(trans_crate, matches, sess, cstore, input, odir, ofile)
148-
}
149-
150-
fn build_controller(
151-
self: Box<Self>,
152-
sess: &Session,
153-
matches: &getopts::Matches,
154-
) -> driver::CompileController<'a> {
155-
let default = self.get_default();
156-
let version = self.get_version().clone();
157-
let api_guidelines = self.get_api_guidelines();
158-
let Self { verbose, .. } = *self;
159-
let mut controller = CompilerCalls::build_controller(default, sess, matches);
160-
let old_callback = std::mem::replace(&mut controller.after_analysis.callback, box |_| {});
161-
162-
controller.after_analysis.callback = box move |state| {
163-
debug!("running rust-semverver after_analysis callback");
164-
callback(state, &version, verbose, api_guidelines);
165-
debug!("running other after_analysis callback");
166-
old_callback(state);
167-
};
168-
controller.after_analysis.stop = Compilation::Stop;
169-
170-
controller
171-
}
27+
/// Display semverver version.
28+
fn show_version() {
29+
println!(env!("CARGO_PKG_VERSION"));
17230
}
17331

17432
/// Main routine.
17533
///
17634
/// Find the sysroot before passing our args to the compiler driver, after registering our custom
17735
/// compiler driver.
17836
fn main() {
179-
if env_logger::try_init().is_err() {
180-
eprintln!("ERROR: could not initialize logger");
181-
}
37+
rustc_driver::init_rustc_env_logger();
18238

18339
debug!("running rust-semverver compiler driver");
40+
exit(
41+
rustc_driver::run(move || {
42+
use std::env;
43+
44+
if std::env::args().any(|a| a == "--version" || a == "-V") {
45+
show_version();
46+
exit(0);
47+
}
48+
49+
let sys_root = option_env!("SYSROOT")
50+
.map(String::from)
51+
.or_else(|| std::env::var("SYSROOT").ok())
52+
.or_else(|| {
53+
let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
54+
let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
55+
home.and_then(|home| toolchain.map(|toolchain| format!("{}/toolchains/{}", home, toolchain)))
56+
})
57+
.or_else(|| {
58+
Command::new("rustc")
59+
.arg("--print")
60+
.arg("sysroot")
61+
.output()
62+
.ok()
63+
.and_then(|out| String::from_utf8(out.stdout).ok())
64+
.map(|s| s.trim().to_owned())
65+
})
66+
.expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");
67+
68+
// Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
69+
// We're invoking the compiler programmatically, so we ignore this/
70+
let mut orig_args: Vec<String> = env::args().collect();
71+
if orig_args.len() <= 1 {
72+
std::process::exit(1);
73+
}
74+
75+
if Path::new(&orig_args[1]).file_stem() == Some("rustc".as_ref()) {
76+
// we still want to be able to invoke it normally though
77+
orig_args.remove(1);
78+
}
79+
80+
// this conditional check for the --sysroot flag is there so users can call
81+
// `clippy_driver` directly
82+
// without having to pass --sysroot or anything
83+
let args: Vec<String> = if orig_args.iter().any(|s| s == "--sysroot") {
84+
orig_args.clone()
85+
} else {
86+
orig_args
87+
.clone()
88+
.into_iter()
89+
.chain(Some("--sysroot".to_owned()))
90+
.chain(Some(sys_root))
91+
.collect()
92+
};
93+
94+
let verbose = std::env::var("RUST_SEMVER_VERBOSE") == Ok("true".to_string());
95+
let api_guidelines = std::env::var("RUST_SEMVER_API_GUIDELINES") == Ok("true".to_string());
96+
let version = if let Ok(ver) = std::env::var("RUST_SEMVER_CRATE_VERSION") {
97+
ver
98+
} else {
99+
"no_version".to_owned()
100+
};
184101

185-
let home = option_env!("RUSTUP_HOME");
186-
let toolchain = option_env!("RUSTUP_TOOLCHAIN");
187-
let sys_root: PathBuf = if let (Some(home), Some(toolchain)) = (home, toolchain) {
188-
Path::new(home).join("toolchains").join(toolchain)
189-
} else {
190-
option_env!("SYSROOT")
191-
.map(|s| s.to_owned())
192-
.or_else(|| {
193-
Command::new("rustc")
194-
.args(&["--print", "sysroot"])
195-
.output()
196-
.ok()
197-
.and_then(|out| String::from_utf8(out.stdout).ok())
198-
.map(|s| s.trim().to_owned())
199-
})
200-
.expect("need to specify SYSROOT env var during compilation, or use rustup")
201-
.into()
202-
};
203-
204-
assert!(
205-
sys_root.exists(),
206-
"sysroot \"{}\" does not exist",
207-
sys_root.display()
208-
);
209-
210-
let result = rustc_driver::run(move || {
211-
let args: Vec<String> = if std::env::args().any(|s| s == "--sysroot") {
212-
std::env::args().collect()
213-
} else {
214-
std::env::args()
215-
.chain(Some("--sysroot".to_owned()))
216-
.chain(Some(sys_root.to_str().unwrap().to_owned()))
217-
.collect()
218-
};
219-
220-
let version = if let Ok(ver) = std::env::var("RUST_SEMVER_CRATE_VERSION") {
221-
ver
222-
} else {
223-
"no_version".to_owned()
224-
};
225-
226-
let verbose = std::env::var("RUST_SEMVER_VERBOSE") == Ok("true".to_string());
227-
let api_guidelines = std::env::var("RUST_SEMVER_API_GUIDELINES") == Ok("true".to_string());
228-
229-
let cc = SemVerVerCompilerCalls::new(version, verbose, api_guidelines);
230-
rustc_driver::run_compiler(&args, cc, None, None)
231-
});
232-
233-
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_possible_truncation))]
234-
std::process::exit(result as i32);
102+
let mut controller = CompileController::basic();
103+
104+
controller.after_analysis.callback = Box::new(move |state| {
105+
debug!("running rust-semverver after_analysis...");
106+
let tcx = state.tcx.unwrap();
107+
108+
// To select the old and new crates we look at the position of the declaration in the
109+
// source file. The first one will be the `old` and the other will be `new`. This is
110+
// unfortunately a bit hacky... See issue #64 for details.
111+
112+
let mut crates: Vec<_> = tcx
113+
.crates()
114+
.iter()
115+
.flat_map(|crate_num| {
116+
let def_id = DefId {
117+
krate: *crate_num,
118+
index: CRATE_DEF_INDEX,
119+
};
120+
121+
match *tcx.extern_crate(def_id) {
122+
Some(ExternCrate {
123+
span, direct: true, ..
124+
}) if span.data().lo.to_usize() > 0 => Some((span.data().lo.to_usize(), def_id)),
125+
_ => None,
126+
}
127+
})
128+
.collect();
129+
130+
crates.sort_by_key(|&(span_lo, _)| span_lo);
131+
132+
if let [(_, old_def_id), (_, new_def_id)] = *crates.as_slice() {
133+
debug!("running semver analysis");
134+
let changes = run_analysis(tcx, old_def_id, new_def_id);
135+
changes.output(tcx.sess, &version, verbose, api_guidelines);
136+
} else {
137+
tcx.sess.err("could not find crate old and new crates");
138+
}
139+
140+
debug!("running rust-semverver after_analysis finished!");
141+
});
142+
controller.after_analysis.stop = Compilation::Stop;
143+
144+
let args = args;
145+
rustc_driver::run_compiler(&args, Box::new(controller), None, None)
146+
}).try_into()
147+
.expect("exit code too large"),
148+
)
235149
}

0 commit comments

Comments
 (0)