Skip to content

Commit ae7ae57

Browse files
committed
Implemented a basic vesion of exported type querying.
As requested in rust-lang#63, this adds a way to list all items in the type namespace of a crate.
1 parent 0281613 commit ae7ae57

File tree

6 files changed

+236
-20
lines changed

6 files changed

+236
-20
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ path = "src/bin/cargo_semver.rs"
2222
name = "rust-semverver"
2323
path = "src/bin/rust_semverver.rs"
2424

25+
[[bin]]
26+
name = "rust-semver-public"
27+
path = "src/bin/rust_semver_public.rs"
28+
2529
[dependencies]
2630
cargo = "0.36"
2731
crates-io = "0.24"

src/bin/cargo_semver.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,52 @@ fn run(config: &cargo::Config, matches: &getopts::Matches) -> Result<()> {
107107
};
108108
let name = current.package.name().to_owned();
109109

110+
if matches.opt_present("show-public") {
111+
let (current_rlib, current_deps_output) =
112+
current.rlib_and_dep_output(config, &name, true, matches)?;
113+
114+
let mut child = Command::new("rust-semver-public");
115+
child
116+
.arg("--crate-type=lib")
117+
.args(&["--extern", &*format!("new={}", current_rlib.display())])
118+
.args(&[format!("-L{}", current_deps_output.display())]);
119+
120+
if let Some(target) = matches.opt_str("target") {
121+
child.args(&["--target", &target]);
122+
}
123+
124+
if !matches.opt_present("no-default-features") {
125+
child.args(&["--cfg", "feature=\"default\""]);
126+
}
127+
128+
let mut child = child
129+
.arg("-")
130+
.stdin(Stdio::piped())
131+
.spawn()
132+
.map_err(|e| failure::err_msg(format!("could not spawn rustc: {}", e)))?;
133+
134+
if let Some(ref mut stdin) = child.stdin {
135+
stdin.write_fmt(format_args!(
136+
"#[allow(unused_extern_crates)] \
137+
extern crate new;"
138+
))?;
139+
} else {
140+
return Err(failure::err_msg(
141+
"could not pipe to rustc (wtf?)".to_owned(),
142+
));
143+
}
144+
145+
let exit_status = child
146+
.wait()
147+
.map_err(|e| failure::err_msg(format!("failed to wait for rustc: {}", e)))?;
148+
149+
return if exit_status.success() {
150+
Ok(())
151+
} else {
152+
Err(failure::err_msg("rustc-semver-public errored".to_owned()))
153+
};
154+
}
155+
110156
// Obtain WorkInfo for the "stable" version
111157
let (stable, stable_version) = if let Some(name_and_version) = matches.opt_str("S") {
112158
// -S "name:version" requires fetching the appropriate package:
@@ -227,6 +273,11 @@ mod cli {
227273
"quiet",
228274
"surpress regular cargo output, print only important messages",
229275
);
276+
opts.optflag(
277+
"",
278+
"show-public",
279+
"print the public types in the current crate given by -c or -C and exit",
280+
);
230281
opts.optflag("d", "debug", "print command to debug and exit");
231282
opts.optflag(
232283
"a",

src/bin/rust_semver_public.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#![feature(rustc_private)]
2+
#![feature(result_map_or_else)]
3+
4+
extern crate rustc;
5+
extern crate rustc_driver;
6+
extern crate rustc_interface;
7+
extern crate syntax;
8+
9+
use log::debug;
10+
use rustc::{hir::def_id::*, middle::cstore::ExternCrate};
11+
use rustc_driver::Callbacks;
12+
use rustc_interface::interface;
13+
use semverver::run_traversal;
14+
use std::{
15+
path::Path,
16+
process::{exit, Command},
17+
};
18+
use syntax::source_map::Pos;
19+
20+
/// Display semverver version.
21+
fn show_version() {
22+
println!(env!("CARGO_PKG_VERSION"));
23+
}
24+
25+
/// Main routine.
26+
///
27+
/// Find the sysroot before passing our args to the custom compiler driver we register.
28+
fn main() {
29+
rustc_driver::init_rustc_env_logger();
30+
31+
debug!("running rust-semver-public compiler driver");
32+
33+
exit(
34+
{
35+
use std::env;
36+
37+
struct PubCallbacks;
38+
39+
impl Callbacks for PubCallbacks {
40+
fn after_analysis(&mut self, compiler: &interface::Compiler) -> bool {
41+
debug!("running rust-semver-public after_analysis callback");
42+
43+
compiler.global_ctxt().unwrap().peek_mut().enter(|tcx| {
44+
let krate = tcx
45+
.crates()
46+
.iter()
47+
.flat_map(|crate_num| {
48+
let def_id = DefId {
49+
krate: *crate_num,
50+
index: CRATE_DEF_INDEX,
51+
};
52+
53+
match tcx.extern_crate(def_id) {
54+
Some(ExternCrate {
55+
span, direct: true, ..
56+
}) if span.data().lo.to_usize() > 0 => Some(def_id),
57+
_ => None,
58+
}
59+
})
60+
.next();
61+
62+
if let Some(krate_def_id) = krate {
63+
debug!("running semver analysis");
64+
run_traversal(tcx, krate_def_id);
65+
} else {
66+
tcx.sess.err("could not find `new` crate");
67+
}
68+
});
69+
70+
debug!("rust-semver-public after_analysis callback finished!");
71+
72+
false
73+
}
74+
}
75+
76+
if env::args().any(|a| a == "--version" || a == "-V") {
77+
show_version();
78+
exit(0);
79+
}
80+
81+
let sys_root = option_env!("SYSROOT")
82+
.map(String::from)
83+
.or_else(|| env::var("SYSROOT").ok())
84+
.or_else(|| {
85+
let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
86+
let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
87+
home.and_then(|home| toolchain.map(|toolchain| format!("{}/toolchains/{}", home, toolchain)))
88+
})
89+
.or_else(|| {
90+
Command::new("rustc")
91+
.arg("--print")
92+
.arg("sysroot")
93+
.output()
94+
.ok()
95+
.and_then(|out| String::from_utf8(out.stdout).ok())
96+
.map(|s| s.trim().to_owned())
97+
})
98+
.expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");
99+
100+
// Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
101+
// We're invoking the compiler programmatically, so we ignore this/
102+
let mut orig_args: Vec<String> = env::args().collect();
103+
if orig_args.len() <= 1 {
104+
std::process::exit(1);
105+
}
106+
107+
if Path::new(&orig_args[1]).file_stem() == Some("rustc".as_ref()) {
108+
// we still want to be able to invoke it normally though
109+
orig_args.remove(1);
110+
}
111+
112+
// this conditional check for the --sysroot flag is there so users can call
113+
// `clippy_driver` directly
114+
// without having to pass --sysroot or anything
115+
let args: Vec<String> = if orig_args.iter().any(|s| s == "--sysroot") {
116+
orig_args.clone()
117+
} else {
118+
orig_args
119+
.clone()
120+
.into_iter()
121+
.chain(Some("--sysroot".to_owned()))
122+
.chain(Some(sys_root))
123+
.collect()
124+
};
125+
126+
let args = args;
127+
rustc_driver::run_compiler(&args, &mut PubCallbacks, None, None)
128+
}
129+
.map_or_else(|_| 1, |_| 0),
130+
)
131+
}

src/bin/rust_semverver.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#![feature(rustc_private)]
22
#![feature(result_map_or_else)]
33

4-
extern crate getopts;
54
extern crate rustc;
65
extern crate rustc_codegen_utils;
76
extern crate rustc_driver;
@@ -28,8 +27,7 @@ fn show_version() {
2827

2928
/// Main routine.
3029
///
31-
/// Find the sysroot before passing our args to the compiler driver, after registering our custom
32-
/// compiler driver.
30+
/// Find the sysroot before passing our args to the custom compiler driver we register.
3331
fn main() {
3432
rustc_driver::init_rustc_env_logger();
3533

@@ -45,12 +43,12 @@ fn main() {
4543
debug!("running rust-semverver after_analysis callback");
4644

4745
let verbose =
48-
std::env::var("RUST_SEMVER_VERBOSE") == Ok("true".to_string());
46+
env::var("RUST_SEMVER_VERBOSE") == Ok("true".to_string());
4947
let compact =
50-
std::env::var("RUST_SEMVER_COMPACT") == Ok("true".to_string());
48+
env::var("RUST_SEMVER_COMPACT") == Ok("true".to_string());
5149
let api_guidelines =
52-
std::env::var("RUST_SEMVER_API_GUIDELINES") == Ok("true".to_string());
53-
let version = if let Ok(ver) = std::env::var("RUST_SEMVER_CRATE_VERSION") {
50+
env::var("RUST_SEMVER_API_GUIDELINES") == Ok("true".to_string());
51+
let version = if let Ok(ver) = env::var("RUST_SEMVER_CRATE_VERSION") {
5452
ver
5553
} else {
5654
"no_version".to_owned()
@@ -88,7 +86,7 @@ fn main() {
8886
let changes = run_analysis(tcx, old_def_id, new_def_id);
8987
changes.output(tcx.sess, &version, verbose, compact, api_guidelines);
9088
} else {
91-
tcx.sess.err("could not find crate old and new crates");
89+
tcx.sess.err("could not find `old` and `new` crates");
9290
}
9391
});
9492

@@ -98,14 +96,14 @@ fn main() {
9896
}
9997
}
10098

101-
if std::env::args().any(|a| a == "--version" || a == "-V") {
99+
if env::args().any(|a| a == "--version" || a == "-V") {
102100
show_version();
103101
exit(0);
104102
}
105103

106104
let sys_root = option_env!("SYSROOT")
107105
.map(String::from)
108-
.or_else(|| std::env::var("SYSROOT").ok())
106+
.or_else(|| env::var("SYSROOT").ok())
109107
.or_else(|| {
110108
let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
111109
let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ mod translate;
1414
mod traverse;
1515
mod typeck;
1616

17-
pub use self::traverse::run_analysis;
17+
pub use self::traverse::{run_analysis, run_traversal};

src/traverse.rs

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,47 @@ pub fn run_analysis<'a, 'tcx>(
6969
changes
7070
}
7171

72+
// Get the visibility of the inner item, given the outer item's visibility.
73+
fn get_vis(outer_vis: Visibility, def: Export<HirId>) -> Visibility {
74+
if outer_vis == Public {
75+
def.vis
76+
} else {
77+
outer_vis
78+
}
79+
}
80+
81+
pub fn run_traversal<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, new: DefId) {
82+
use rustc::hir::def::DefKind::*;
83+
let mut visited = HashSet::new();
84+
let mut mod_queue = VecDeque::new();
85+
86+
// Start off with the root module.
87+
mod_queue.push_back((new, Public));
88+
89+
// Pull a module from the queue, with its global visibility.
90+
while let Some((new_def_id, new_vis)) = mod_queue.pop_front() {
91+
for item in tcx.item_children(new_def_id).to_vec() {
92+
let n_vis = get_vis(new_vis, item);
93+
match item.res {
94+
Def(Mod, n_def_id) => {
95+
if visited.insert(n_def_id) {
96+
mod_queue.push_back((n_def_id, n_vis));
97+
}
98+
},
99+
Def(n_kind, n_def_id) if n_vis == Public => {
100+
match n_kind {
101+
TyAlias | Struct | Union | Enum | Trait => {
102+
println!("{:?}", n_def_id);
103+
},
104+
_ => (),
105+
};
106+
},
107+
_ => (),
108+
}
109+
}
110+
}
111+
}
112+
72113
// Below functions constitute the first pass of analysis, in which module structure, ADT
73114
// structure, public and private status of items, and generics are examined for changes.
74115

@@ -87,15 +128,6 @@ fn diff_structure<'a, 'tcx>(
87128
) {
88129
use rustc::hir::def::DefKind::*;
89130

90-
// Get the visibility of the inner item, given the outer item's visibility.
91-
fn get_vis(outer_vis: Visibility, def: Export<HirId>) -> Visibility {
92-
if outer_vis == Public {
93-
def.vis
94-
} else {
95-
outer_vis
96-
}
97-
}
98-
99131
let mut visited = HashSet::new();
100132
let mut children = NameMapping::default();
101133
let mut mod_queue = VecDeque::new();

0 commit comments

Comments
 (0)