diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 73ad23d..345dd65 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,8 +13,38 @@ env: CARGO_TERM_COLOR: always jobs: + code-quality: + name: "Code Quality" + runs-on: [self-hosted, linux, normal] + steps: + - name: 'Check out code' + uses: actions/checkout@v4 + with: + # Check out pull request HEAD instead of merge commit. + ref: ${{ github.event.pull_request.head.sha }} + submodules: recursive + + - name: "Set up nightly Rust" # https://github.com/rust-lang/rustup/issues/3409 + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2024-11-29 # Hardcoded version, same as is in the build.rs + + - name: 'Build smir_pretty' # rustfmt documentation claims it is unstable on code that doesn't build + run: | + cargo build -vv + + - name: "Check `cargo clippy`" + run: | + rustup component add clippy + cargo clippy -- -Dwarnings + + - name: "Check `cargo fmt`" + run: | + rustup component add rustfmt + cargo fmt --check integration-tests: + needs: code-quality name: "Integration tests" runs-on: [self-hosted, linux, normal] steps: @@ -31,8 +61,8 @@ jobs: toolchain: nightly-2024-11-29 # Hardcoded version, same as is in the build.rs - name: 'Build smir_pretty' - run: | - cargo build -vv + run: | # Warning check should be redundant since code-quality runs first + RUSTFLAGS='--deny warnings' cargo build -vv - name: 'Run smir integration tests' run: | diff --git a/README.md b/README.md index 05cec43..684c938 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,10 @@ There are a few environment variables that can be set to control the tools outpu 2. `LINK_INST` - use a richer key-structure for the link-time `functions` map which uses keys that are pairs of a function type (`Ty`) _and_ an function instance kind (`InstanceKind`) 3. `DEBUG` - serialize additional data in the JSON file and dump logs to stdout +## Development + +To ensure code quality, all code is required to pass `cargo clippy` and `cargo fmt` without warning to pass CI. + ## Tests ### Running the Tests diff --git a/build.rs b/build.rs index 806e893..2b45b31 100644 --- a/build.rs +++ b/build.rs @@ -2,30 +2,30 @@ use std::process::Command; fn main() { let status = Command::new("rustup") - .args(&["install", "nightly-2024-11-29"]) + .args(["install", "nightly-2024-11-29"]) .status() .expect("build.rs failed to install nightly-2024-11-29"); println!("Installed nightly-2024-11-29: {}", status); let status = Command::new("rustup") - .args(&["default", "nightly-2024-11-29"]) + .args(["default", "nightly-2024-11-29"]) .status() .expect("build.rs failed to default nightly-2024-11-29"); println!("Defaulted nightly-2024-11-29: {}", status); let status = Command::new("rustup") - .args(&["component", "add", "rustc-dev"]) + .args(["component", "add", "rustc-dev"]) .status() .expect("build.rs failed to install rustc-dev"); println!("Added component rustc-dev: {}", status); let status = Command::new("rustup") - .args(&["component", "add", "llvm-tools"]) + .args(["component", "add", "llvm-tools"]) .status() .expect("build.rs failed to install llvm-tools"); println!("Added component llvm-tools: {}", status); -} \ No newline at end of file +} diff --git a/src/driver.rs b/src/driver.rs index 7406e10..7522ebd 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -17,36 +17,32 @@ //! //! However, we prefer a non-macro version for clarity and build simplicity. -extern crate rustc_middle; extern crate rustc_driver; extern crate rustc_interface; -extern crate rustc_smir; +extern crate rustc_middle; extern crate rustc_session; -use rustc_middle::ty::TyCtxt; +extern crate rustc_smir; use rustc_driver::Compilation; use rustc_interface::interface::Compiler; +use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; struct StableMirCallbacks { - callback_fn: fn (TyCtxt) -> (), + callback_fn: fn(TyCtxt) -> (), } impl rustc_driver::Callbacks for StableMirCallbacks { - fn after_analysis<'tcx>( - &mut self, - _compiler: &Compiler, - tcx: TyCtxt<'tcx>, - ) -> Compilation { - + fn after_analysis(&mut self, _compiler: &Compiler, tcx: TyCtxt) -> Compilation { let _ = rustc_internal::run(tcx, || (self.callback_fn)(tcx)); Compilation::Continue } } -pub fn stable_mir_driver(args_outer: &Vec, callback_fn: fn (TyCtxt) -> ()) { +pub fn stable_mir_driver(args_outer: &[String], callback_fn: fn(TyCtxt) -> ()) { let mut callbacks = StableMirCallbacks { callback_fn }; - let early_dcx = rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default()); + let early_dcx = + rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default()); rustc_driver::init_rustc_env_logger(&early_dcx); let _ = rustc_driver::RunCompiler::new(args_outer, &mut callbacks).run(); } diff --git a/src/main.rs b/src/main.rs index 231bec1..ea72d5c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,8 +10,7 @@ fn main() { let mut args: Vec = env::args().collect(); match args.get(1) { - None => - stable_mir_driver(&args, emit_smir), // backward compatibility + None => stable_mir_driver(&args, emit_smir), // backward compatibility Some(arg) if arg == "--json" => { args.remove(1); stable_mir_driver(&args, emit_smir) @@ -20,7 +19,6 @@ fn main() { args.remove(1); stable_mir_driver(&args, emit_dotfile) } - Some(_other) => - stable_mir_driver(&args, emit_smir), // backward compatibility + Some(_other) => stable_mir_driver(&args, emit_smir), // backward compatibility } } diff --git a/src/mk_graph.rs b/src/mk_graph.rs index 0411450..2c57e3e 100644 --- a/src/mk_graph.rs +++ b/src/mk_graph.rs @@ -1,4 +1,9 @@ -use std::{collections::{HashMap, HashSet}, fs::File, hash::{DefaultHasher, Hash, Hasher}, io::{self, Write}}; +use std::{ + collections::{HashMap, HashSet}, + fs::File, + hash::{DefaultHasher, Hash, Hasher}, + io::{self, Write}, +}; use dot_writer::{Attributes, Color, DotWriter, Scope, Style}; @@ -9,323 +14,353 @@ extern crate stable_mir; use rustc_session::config::{OutFileName, OutputType}; extern crate rustc_session; +use stable_mir::mir::{BasicBlock, ConstOperand, Operand, Place, TerminatorKind, UnwindAction}; use stable_mir::ty::Ty; -use stable_mir::mir::{ - BasicBlock, - ConstOperand, - Operand, - Place, - Statement, - TerminatorKind, - UnwindAction, -}; -use crate::{printer::{FnSymType, SmirJson, collect_smir}, MonoItemKind}; +use crate::{ + printer::{collect_smir, FnSymType, SmirJson}, + MonoItemKind, +}; // entry point to write the dot file pub fn emit_dotfile(tcx: TyCtxt<'_>) { + let smir_dot = collect_smir(tcx).to_dot_file(); - let smir_dot = collect_smir(tcx).to_dot_file(); - - match tcx.output_filenames(()).path(OutputType::Mir) { - OutFileName::Stdout => { - write!(io::stdout(), "{}", smir_dot).expect("Failed to write smir.dot"); - } - OutFileName::Real(path) => { - let mut b = - io::BufWriter::new( - File::create(&path.with_extension("smir.dot")) - .expect("Failed to create {path}.smir.dot output file")); - write!(b, "{}", smir_dot).expect("Failed to write smir.dot"); + match tcx.output_filenames(()).path(OutputType::Mir) { + OutFileName::Stdout => { + write!(io::stdout(), "{}", smir_dot).expect("Failed to write smir.dot"); + } + OutFileName::Real(path) => { + let mut b = io::BufWriter::new( + File::create(path.with_extension("smir.dot")) + .expect("Failed to create {path}.smir.dot output file"), + ); + write!(b, "{}", smir_dot).expect("Failed to write smir.dot"); + } } - } } impl SmirJson<'_> { + pub fn to_dot_file(self) -> String { + let mut bytes = Vec::new(); - pub fn to_dot_file(self) -> String { - let mut bytes = Vec::new(); - - { - let mut writer = DotWriter::from(&mut bytes); - - writer.set_pretty_print(true); - - let mut graph = writer.digraph(); - graph.set_label(&self.name[..]); - - let func_map: HashMap = - self.functions - .into_iter() - .map(|(k,v)| (k.0, function_string(v))) - .collect(); - - let item_names: HashSet = - self.items - .iter() - .map(|i| i.symbol_name.clone()) - .collect(); - - // first create all nodes for functions not in the items list - for f in func_map.values() { - if ! item_names.contains(f) { - graph - .node_named(block_name(f, 0)) - .set_label(&name_lines(f)) - .set_color(Color::Red); - } - } - - for item in self.items { - match item.mono_item_kind { - MonoItemKind::MonoItemFn{ name, body, id: _} => { - let mut c = graph.cluster(); - c.set_label(&name_lines(&name)); - if is_unqualified(&name) { - c.set_style(Style::Filled); - c.set_color(Color::LightGrey); - } + { + let mut writer = DotWriter::from(&mut bytes); - // Cannot define local functions that capture env. variables. Instead we define _closures_. - let process_block = |cluster:&mut Scope<'_,'_>, node_id: usize, b: &BasicBlock | { - let name = &item.symbol_name; - let this_block = block_name(name, node_id); - let mut n = cluster.node_named(&this_block); - // TODO: render statements and terminator as text label (with line breaks) - // switch on terminator kind, add inner and out-edges according to terminator - use TerminatorKind::*; - match &b.terminator.kind { + writer.set_pretty_print(true); - Goto{target} => { - n.set_label("Goto"); - drop(n); // so we can borrow `cluster` again below - cluster.edge(&this_block, block_name(name, *target)); - }, - SwitchInt{discr:_, targets} => { - n.set_label("SwitchInt"); - drop(n); // so we can borrow `cluster` again below - for (d,t) in targets.clone().branches() { - cluster - .edge(&this_block, block_name(name, t)) - .attributes() - .set_label(&format!("{d}")); - }; - cluster - .edge(&this_block, block_name(name, targets.otherwise())) - .attributes() - .set_label("other"); - }, - Resume{} => { - n.set_label("Resume"); - }, - Abort{} => { - n.set_label("Abort"); - }, - Return{} => { - n.set_label("Return"); - }, - Unreachable{} => { - n.set_label("Unreachable"); - }, - TerminatorKind::Drop{place, target, unwind} => { - n.set_label(&format!("Drop {}", show_place(place))); - drop(n); - if let UnwindAction::Cleanup(t) = unwind { - cluster - .edge(&this_block, block_name(name, *t)) - .attributes() - .set_label("Cleanup"); - } - cluster - .edge(&this_block, block_name(name, *target)); - }, - Call{func: _, args: _, destination, target, unwind} => { - n.set_label(&format!("Call()")); - drop(n); - if let UnwindAction::Cleanup(t) = unwind { - cluster - .edge(&this_block, block_name(name, *t)) - .attributes() - .set_label("Cleanup"); - } - if let Some(t) = target { - let dest = show_place(destination); - cluster - .edge(&this_block, block_name(name, *t)) - .attributes() - .set_label(&dest); - } + let mut graph = writer.digraph(); + graph.set_label(&self.name[..]); - // The call edge has to be drawn outside the cluster, outside this function (cluster borrows &mut graph)! - // Code for that is therefore separated into its own second function below. - }, - Assert{target, ..} => { - n.set_label("Assert"); - drop(n); - cluster - .edge(&this_block, block_name(name, *target)); - }, - InlineAsm{destination, unwind,..} => { - n.set_label("Inline ASM"); - drop(n); - if let Some(t) = destination { - cluster - .edge(&this_block, block_name(name, *t)); - } - if let UnwindAction::Cleanup(t) = unwind { - cluster - .edge(&this_block, block_name(name, *t)) - .attributes() - .set_label("Cleanup"); - } - } - } - }; + let func_map: HashMap = self + .functions + .into_iter() + .map(|(k, v)| (k.0, function_string(v))) + .collect(); - let process_blocks = |cluster:&mut Scope<'_,'_>, offset, blocks: &Vec| { - let mut n:usize = offset; - for b in blocks { - process_block(cluster, n, b); - n += 1; - } - }; + let item_names: HashSet = + self.items.iter().map(|i| i.symbol_name.clone()).collect(); - match &body.len() { - 0 => { - c.node_auto().set_label(""); - }, - 1 => { - process_blocks(&mut c, 0, &body[0].blocks); - } - _more => { - let mut curr: usize = 0; - for b in &body { - let mut cc = c.cluster(); - process_blocks(&mut cc, curr, &b.blocks); - curr += b.blocks.len(); + // first create all nodes for functions not in the items list + for f in func_map.values() { + if !item_names.contains(f) { + graph + .node_named(block_name(f, 0)) + .set_label(&name_lines(f)) + .set_color(Color::Red); } - } } - drop(c); // so we can borrow graph again - // call edges have to be added _outside_ the cluster of blocks for one function - // because they go between different clusters. Due to a scope/borrow issue, we have - // to make a 2nd pass over the bodies of the item. - let add_call_edges = | graph: &mut Scope<'_,'_>, offset: usize, bs: &Vec | { - for (i, b) in bs.iter().enumerate() { - let this_block = block_name(&item.symbol_name, offset + i); - - match &b.terminator.kind { - TerminatorKind::Call{func, args, ..} => { - let e = match func { - Operand::Constant(ConstOperand{const_, ..}) => { - if let Some(callee) = func_map.get(&const_.ty()) { - // callee node/body will be added when its body is added, missing ones added before - graph - .edge(&this_block, block_name(callee, 0)) - } else { - let unknown = format!("{}", const_.ty()); - // pathological case, could panic! instead. - // all unknown callees will be collapsed into one `unknown` node - graph - .edge(&this_block, unknown) + for item in self.items { + match item.mono_item_kind { + MonoItemKind::MonoItemFn { name, body, id: _ } => { + let mut c = graph.cluster(); + c.set_label(&name_lines(&name)); + if is_unqualified(&name) { + c.set_style(Style::Filled); + c.set_color(Color::LightGrey); } - }, - Operand::Copy(place) => { - graph.edge(&this_block, format!("{}: {}", &this_block, show_place(place))) - }, - Operand::Move(place) => { - graph.edge(&this_block, format!("{}: {}", &this_block, show_place(place))) - }, - }; - let arg_str = args.into_iter().map(show_op).collect::>().join(","); - e.attributes().set_label(&arg_str); - }, - _other => { - // nothing to do - }, - } - } - }; - - match &body.len() { - 0 => {}, - 1 => { - add_call_edges(&mut graph, 0, &body[0].blocks); - } - _more => { - let mut curr: usize = 0; - for b in &body { - add_call_edges(&mut graph, curr, &b.blocks); - curr += b.blocks.len(); + // Cannot define local functions that capture env. variables. Instead we define _closures_. + let process_block = + |cluster: &mut Scope<'_, '_>, node_id: usize, b: &BasicBlock| { + let name = &item.symbol_name; + let this_block = block_name(name, node_id); + let mut n = cluster.node_named(&this_block); + // TODO: render statements and terminator as text label (with line breaks) + // switch on terminator kind, add inner and out-edges according to terminator + use TerminatorKind::*; + match &b.terminator.kind { + Goto { target } => { + n.set_label("Goto"); + drop(n); // so we can borrow `cluster` again below + cluster.edge(&this_block, block_name(name, *target)); + } + SwitchInt { discr: _, targets } => { + n.set_label("SwitchInt"); + drop(n); // so we can borrow `cluster` again below + for (d, t) in targets.clone().branches() { + cluster + .edge(&this_block, block_name(name, t)) + .attributes() + .set_label(&format!("{d}")); + } + cluster + .edge( + &this_block, + block_name(name, targets.otherwise()), + ) + .attributes() + .set_label("other"); + } + Resume {} => { + n.set_label("Resume"); + } + Abort {} => { + n.set_label("Abort"); + } + Return {} => { + n.set_label("Return"); + } + Unreachable {} => { + n.set_label("Unreachable"); + } + TerminatorKind::Drop { + place, + target, + unwind, + } => { + n.set_label(&format!("Drop {}", show_place(place))); + drop(n); + if let UnwindAction::Cleanup(t) = unwind { + cluster + .edge(&this_block, block_name(name, *t)) + .attributes() + .set_label("Cleanup"); + } + cluster.edge(&this_block, block_name(name, *target)); + } + Call { + func: _, + args: _, + destination, + target, + unwind, + } => { + n.set_label("Call()"); + drop(n); + if let UnwindAction::Cleanup(t) = unwind { + cluster + .edge(&this_block, block_name(name, *t)) + .attributes() + .set_label("Cleanup"); + } + if let Some(t) = target { + let dest = show_place(destination); + cluster + .edge(&this_block, block_name(name, *t)) + .attributes() + .set_label(&dest); + } + + // The call edge has to be drawn outside the cluster, outside this function (cluster borrows &mut graph)! + // Code for that is therefore separated into its own second function below. + } + Assert { target, .. } => { + n.set_label("Assert"); + drop(n); + cluster.edge(&this_block, block_name(name, *target)); + } + InlineAsm { + destination, + unwind, + .. + } => { + n.set_label("Inline ASM"); + drop(n); + if let Some(t) = destination { + cluster.edge(&this_block, block_name(name, *t)); + } + if let UnwindAction::Cleanup(t) = unwind { + cluster + .edge(&this_block, block_name(name, *t)) + .attributes() + .set_label("Cleanup"); + } + } + } + }; + + let process_blocks = + |cluster: &mut Scope<'_, '_>, offset, blocks: &Vec| { + let mut n: usize = offset; + for b in blocks { + process_block(cluster, n, b); + n += 1; + } + }; + + match &body.len() { + 0 => { + c.node_auto().set_label(""); + } + 1 => { + process_blocks(&mut c, 0, &body[0].blocks); + } + _more => { + let mut curr: usize = 0; + for b in &body { + let mut cc = c.cluster(); + process_blocks(&mut cc, curr, &b.blocks); + curr += b.blocks.len(); + } + } + } + drop(c); // so we can borrow graph again + + // call edges have to be added _outside_ the cluster of blocks for one function + // because they go between different clusters. Due to a scope/borrow issue, we have + // to make a 2nd pass over the bodies of the item. + let add_call_edges = + |graph: &mut Scope<'_, '_>, offset: usize, bs: &Vec| { + for (i, b) in bs.iter().enumerate() { + let this_block = block_name(&item.symbol_name, offset + i); + + match &b.terminator.kind { + TerminatorKind::Call { func, args, .. } => { + let e = match func { + Operand::Constant(ConstOperand { + const_, .. + }) => { + if let Some(callee) = func_map.get(&const_.ty()) + { + // callee node/body will be added when its body is added, missing ones added before + graph.edge( + &this_block, + block_name(callee, 0), + ) + } else { + let unknown = format!("{}", const_.ty()); + // pathological case, could panic! instead. + // all unknown callees will be collapsed into one `unknown` node + graph.edge(&this_block, unknown) + } + } + Operand::Copy(place) => graph.edge( + &this_block, + format!( + "{}: {}", + &this_block, + show_place(place) + ), + ), + Operand::Move(place) => graph.edge( + &this_block, + format!( + "{}: {}", + &this_block, + show_place(place) + ), + ), + }; + let arg_str = args + .iter() + .map(show_op) + .collect::>() + .join(","); + e.attributes().set_label(&arg_str); + } + _other => { + // nothing to do + } + } + } + }; + + match &body.len() { + 0 => {} + 1 => { + add_call_edges(&mut graph, 0, &body[0].blocks); + } + _more => { + let mut curr: usize = 0; + for b in &body { + add_call_edges(&mut graph, curr, &b.blocks); + curr += b.blocks.len(); + } + } + } + } + MonoItemKind::MonoItemGlobalAsm { asm } => { + let mut n = graph.node_named(short_name(&asm)); + n.set_label(&asm.lines().collect::()[..]); + } + MonoItemKind::MonoItemStatic { + name, + id: _, + allocation: _, + } => { + let mut n = graph.node_named(short_name(&name)); + n.set_label(&name[..]); + } } - } } - - } - MonoItemKind::MonoItemGlobalAsm { asm } => { - let mut n = graph.node_named(short_name(&asm)); - n.set_label(&asm.lines().collect::()[..]); - } - MonoItemKind::MonoItemStatic { name, id: _, allocation: _ } => { - let mut n = graph.node_named(short_name(&name)); - n.set_label(&name[..]); - } } - } - + String::from_utf8(bytes).expect("Error converting dot file") } - - String::from_utf8(bytes).expect("Error converting dot file") - } - } fn show_op(op: &Operand) -> String { - match op { - Operand::Constant(ConstOperand{const_, ..}) => format!("const :: {}", const_.ty()), - Operand::Copy(place) => show_place(place), - Operand::Move(place) => show_place(place), - } + match op { + Operand::Constant(ConstOperand { const_, .. }) => format!("const :: {}", const_.ty()), + Operand::Copy(place) => show_place(place), + Operand::Move(place) => show_place(place), + } } fn show_place(p: &Place) -> String { - format!("_{}{}", p.local, if p.projection.len() > 0 { "(...)"} else {""}) + format!( + "_{}{}", + p.local, + if !p.projection.is_empty() { + "(...)" + } else { + "" + } + ) } -fn is_unqualified(name: &String) -> bool { - ! name.contains("::") +fn is_unqualified(name: &str) -> bool { + !name.contains("::") } fn function_string(f: FnSymType) -> String { - match f { - FnSymType::NormalSym(name) => name, - FnSymType::NoOpSym(name) => format!("NoOp: {name}"), - FnSymType::IntrinsicSym(name) => format!("Intr: {name}"), - } + match f { + FnSymType::NormalSym(name) => name, + FnSymType::NoOpSym(name) => format!("NoOp: {name}"), + FnSymType::IntrinsicSym(name) => format!("Intr: {name}"), + } } -fn name_lines(name: &String) -> String { - name - .split_inclusive(" ") - .flat_map(|s| s.split_inclusive("::")) - .map(|s| s.to_string()) - .collect::>() - .join("\\n") +fn name_lines(name: &str) -> String { + name.split_inclusive(" ") + .flat_map(|s| s.split_inclusive("::")) + .map(|s| s.to_string()) + .collect::>() + .join("\\n") } /// consistently naming function clusters fn short_name(function_name: &String) -> String { - let mut h = DefaultHasher::new(); - function_name.hash(&mut h); - format!("X{:x}", h.finish()) + let mut h = DefaultHasher::new(); + function_name.hash(&mut h); + format!("X{:x}", h.finish()) } /// consistently naming block nodes in function clusters fn block_name(function_name: &String, id: usize) -> String { - let mut h = DefaultHasher::new(); - function_name.hash(&mut h); - format!("X{:x}_{}", h.finish(), id) + let mut h = DefaultHasher::new(); + function_name.hash(&mut h); + format!("X{:x}_{}", h.finish(), id) } diff --git a/src/printer.rs b/src/printer.rs index 0db98ff..a851334 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -1,10 +1,10 @@ use std::io::Write; -use std::{collections::HashMap,fs::File,io,iter::Iterator,vec::Vec,str,}; +use std::{collections::HashMap, fs::File, io, iter::Iterator, str, vec::Vec}; extern crate rustc_middle; extern crate rustc_monomorphize; extern crate rustc_session; -extern crate rustc_span; extern crate rustc_smir; +extern crate rustc_span; extern crate stable_mir; // HACK: typically, we would source serde/serde_json separately from the compiler // However, due to issues matching crate versions when we have our own serde @@ -12,19 +12,22 @@ extern crate stable_mir; extern crate serde; extern crate serde_json; use rustc_middle as middle; -use rustc_middle::ty::{TyCtxt, Ty, TyKind, EarlyBinder, FnSig, GenericArgs, TypeFoldable, TypingEnv}; +use rustc_middle::ty::{ + EarlyBinder, FnSig, GenericArgs, Ty, TyCtxt, TyKind, TypeFoldable, TypingEnv, +}; use rustc_session::config::{OutFileName, OutputType}; -use rustc_span::{def_id::{DefId, LOCAL_CRATE}, symbol}; use rustc_smir::rustc_internal; -use stable_mir::{ - CrateItem, - CrateDef, - ItemKind, - mir::{Body,LocalDecl,Terminator,TerminatorKind,Rvalue,alloc::AllocId,visit::MirVisitor}, - ty::{Allocation,ConstDef,ForeignItemKind}, - mir::mono::{MonoItem,Instance,InstanceKind} +use rustc_span::{ + def_id::{DefId, LOCAL_CRATE}, + symbol, }; use serde::{Serialize, Serializer}; +use stable_mir::{ + mir::mono::{Instance, InstanceKind, MonoItem}, + mir::{alloc::AllocId, visit::MirVisitor, Body, LocalDecl, Rvalue, Terminator, TerminatorKind}, + ty::{Allocation, ConstDef, ForeignItemKind}, + CrateDef, CrateItem, ItemKind, +}; // Structs for serializing extra details about mono items // ====================================================== @@ -35,26 +38,30 @@ struct BodyDetails { } fn get_body_details(body: &Body) -> BodyDetails { - let mut v = Vec::new(); - let _ = body.dump(&mut v, ""); - BodyDetails { pp: str::from_utf8(&v).unwrap().into() } + let mut v = Vec::new(); + let _ = body.dump(&mut v, ""); + BodyDetails { + pp: str::from_utf8(&v).unwrap().into(), + } } #[derive(Serialize, Clone)] -struct GenericData(Vec<(String,String)>); // Alternatively, GenericData<'a>(Vec<(&'a Generics,GenericPredicates<'a>)>); +struct GenericData(Vec<(String, String)>); // Alternatively, GenericData<'a>(Vec<(&'a Generics,GenericPredicates<'a>)>); fn generic_data(tcx: TyCtxt<'_>, id: DefId) -> GenericData { - let mut v = Vec::new(); - let mut next_id = Some(id); - while let Some(curr_id) = next_id { + let mut v = Vec::new(); + let mut next_id = Some(id); + while let Some(curr_id) = next_id { let params = tcx.generics_of(curr_id); - let preds = tcx.predicates_of(curr_id); - if params.parent != preds.parent { panic!("Generics and predicates parent ids are distinct"); } + let preds = tcx.predicates_of(curr_id); + if params.parent != preds.parent { + panic!("Generics and predicates parent ids are distinct"); + } v.push((format!("{:#?}", params), format!("{:#?}", preds))); next_id = params.parent; - } - v.reverse(); - return GenericData(v); + } + v.reverse(); + GenericData(v) } #[derive(Serialize, Clone)] @@ -72,51 +79,75 @@ struct ItemDetails { // unwrap early binder in a default manner; panic on error fn default_unwrap_early_binder<'tcx, T>(tcx: TyCtxt<'tcx>, id: DefId, v: EarlyBinder<'tcx, T>) -> T - where T: TypeFoldable> +where + T: TypeFoldable>, { - let v_copy = v.clone(); - let body = tcx.optimized_mir(id); - match tcx.try_instantiate_and_normalize_erasing_regions(GenericArgs::identity_for_item(tcx, id), body.typing_env(tcx), v) { - Ok(res) => return res, - Err(err) => { println!("{:?}", err); v_copy.skip_binder() } - } + let v_copy = v.clone(); + let body = tcx.optimized_mir(id); + match tcx.try_instantiate_and_normalize_erasing_regions( + GenericArgs::identity_for_item(tcx, id), + body.typing_env(tcx), + v, + ) { + Ok(res) => res, + Err(err) => { + println!("{:?}", err); + v_copy.skip_binder() + } + } } fn print_type<'tcx>(tcx: TyCtxt<'tcx>, id: DefId, ty: EarlyBinder<'tcx, Ty<'tcx>>) -> String { - // lookup type kind in order to perform case analysis - let kind: &TyKind = ty.skip_binder().kind(); - if let TyKind::FnDef(fun_id, args) = kind { - // since FnDef doesn't contain signature, lookup actual function type - // via getting fn signature with parameters and resolving those parameters - let sig0 = tcx.fn_sig(fun_id); - let body = tcx.optimized_mir(id); - let sig1 = match tcx.try_instantiate_and_normalize_erasing_regions(args, body.typing_env(tcx), sig0) { - Ok(res) => res, - Err(err) => { println!("{:?}", err); sig0.skip_binder() } - }; - let sig2: FnSig<'_> = tcx.instantiate_bound_regions_with_erased(sig1); - format!("\nTyKind(FnDef): {:#?}", sig2) - } else { - let kind = default_unwrap_early_binder(tcx, id, ty); - format!("\nTyKind: {:#?}", kind) - } + // lookup type kind in order to perform case analysis + let kind: &TyKind = ty.skip_binder().kind(); + if let TyKind::FnDef(fun_id, args) = kind { + // since FnDef doesn't contain signature, lookup actual function type + // via getting fn signature with parameters and resolving those parameters + let sig0 = tcx.fn_sig(fun_id); + let body = tcx.optimized_mir(id); + let sig1 = match tcx.try_instantiate_and_normalize_erasing_regions( + args, + body.typing_env(tcx), + sig0, + ) { + Ok(res) => res, + Err(err) => { + println!("{:?}", err); + sig0.skip_binder() + } + }; + let sig2: FnSig<'_> = tcx.instantiate_bound_regions_with_erased(sig1); + format!("\nTyKind(FnDef): {:#?}", sig2) + } else { + let kind = default_unwrap_early_binder(tcx, id, ty); + format!("\nTyKind: {:#?}", kind) + } } fn get_item_details(tcx: TyCtxt<'_>, id: DefId, fn_inst: Option) -> Option { - if debug_enabled() { - Some(ItemDetails { - fn_instance_kind: fn_inst.map(|i| i.kind), - fn_item_kind: fn_inst.map(|i| CrateItem::try_from(i).ok()).flatten().map(|i| i.kind()), - fn_body_details: if let Some(fn_inst) = fn_inst { get_bodies(tcx, &fn_inst).iter().map(get_body_details).collect() } else { vec![] }, - internal_kind: format!("{:#?}", tcx.def_kind(id)), - path: tcx.def_path_str(id), // NOTE: underlying data from tcx.def_path(id); - internal_ty: print_type(tcx, id, tcx.type_of(id)), - generic_data: generic_data(tcx, id), - // TODO: let layout = tcx.layout_of(id); - }) - } else { - None - } + if debug_enabled() { + Some(ItemDetails { + fn_instance_kind: fn_inst.map(|i| i.kind), + fn_item_kind: fn_inst + .and_then(|i| CrateItem::try_from(i).ok()) + .map(|i| i.kind()), + fn_body_details: if let Some(fn_inst) = fn_inst { + get_bodies(tcx, &fn_inst) + .iter() + .map(get_body_details) + .collect() + } else { + vec![] + }, + internal_kind: format!("{:#?}", tcx.def_kind(id)), + path: tcx.def_path_str(id), // NOTE: underlying data from tcx.def_path(id); + internal_ty: print_type(tcx, id, tcx.type_of(id)), + generic_data: generic_data(tcx, id), + // TODO: let layout = tcx.layout_of(id); + }) + } else { + None + } } #[derive(Serialize)] @@ -131,16 +162,34 @@ struct ForeignModule { } fn get_foreign_module_details() -> Vec<(String, Vec)> { - let mut crates = vec![stable_mir::local_crate()]; - crates.append(&mut stable_mir::external_crates()); - crates.into_iter().map(|krate| { - ( krate.name.clone(), - krate.foreign_modules().into_iter().map(|mod_def| { - let fmod = mod_def.module(); - ForeignModule { name: mod_def.name(), items: fmod.items().into_iter().map(|def| ForeignItem { name: def.name(), kind: def.kind() }).collect() } - }).collect::>() - ) - }).collect() + let mut crates = vec![stable_mir::local_crate()]; + crates.append(&mut stable_mir::external_crates()); + crates + .into_iter() + .map(|krate| { + ( + krate.name.clone(), + krate + .foreign_modules() + .into_iter() + .map(|mod_def| { + let fmod = mod_def.module(); + ForeignModule { + name: mod_def.name(), + items: fmod + .items() + .into_iter() + .map(|def| ForeignItem { + name: def.name(), + kind: def.kind(), + }) + .collect(), + } + }) + .collect::>(), + ) + }) + .collect() } // Miscellaneous helper functions @@ -151,68 +200,76 @@ macro_rules! def_env_var { fn $fn_name() -> bool { use std::sync::OnceLock; static VAR: OnceLock = OnceLock::new(); - *VAR.get_or_init(|| { - std::env::var(stringify!($var_name)).is_ok() - }) + *VAR.get_or_init(|| std::env::var(stringify!($var_name)).is_ok()) } }; } -def_env_var!(debug_enabled, DEBUG); -def_env_var!(link_items_enabled, LINK_ITEMS); +def_env_var!(debug_enabled, DEBUG); +def_env_var!(link_items_enabled, LINK_ITEMS); def_env_var!(link_instance_enabled, LINK_INST); // Possible input: sym::test pub fn has_attr(tcx: TyCtxt<'_>, item: &stable_mir::CrateItem, attr: symbol::Symbol) -> bool { - tcx.has_attr(rustc_internal::internal(tcx,item), attr) + tcx.has_attr(rustc_internal::internal(tcx, item), attr) } fn mono_item_name(tcx: TyCtxt<'_>, item: &MonoItem) -> String { - if let MonoItem::GlobalAsm(data) = item { - hash(data).to_string() - } else { - mono_item_name_int(tcx, &rustc_internal::internal(tcx, item)) - } + if let MonoItem::GlobalAsm(data) = item { + hash(data).to_string() + } else { + mono_item_name_int(tcx, &rustc_internal::internal(tcx, item)) + } } fn mono_item_name_int<'a>(tcx: TyCtxt<'a>, item: &rustc_middle::mir::mono::MonoItem<'a>) -> String { - item.symbol_name(tcx).name.into() + item.symbol_name(tcx).name.into() } fn get_promoted(tcx: TyCtxt<'_>, inst: &Instance) -> Vec { - let id = rustc_internal::internal(tcx, inst.def.def_id()); - if inst.has_body() { tcx.promoted_mir(id).into_iter().map(rustc_internal::stable).collect() } else { vec![] } + let id = rustc_internal::internal(tcx, inst.def.def_id()); + if inst.has_body() { + tcx.promoted_mir(id) + .into_iter() + .map(rustc_internal::stable) + .collect() + } else { + vec![] + } } fn get_bodies(tcx: TyCtxt<'_>, inst: &Instance) -> Vec { - if let Some(body) = inst.body() { - let mut bodies = get_promoted(tcx, inst); - bodies.insert(0, body); - bodies - } else { - vec![] - } + if let Some(body) = inst.body() { + let mut bodies = get_promoted(tcx, inst); + bodies.insert(0, body); + bodies + } else { + vec![] + } } fn fn_inst_for_ty(ty: stable_mir::ty::Ty, direct_call: bool) -> Option { - ty.kind().fn_def().map(|(fn_def, args)| { - if direct_call { - Instance::resolve(fn_def, args) - } else { - Instance::resolve_for_fn_ptr(fn_def, args) - }.ok() - }).flatten() + ty.kind().fn_def().and_then(|(fn_def, args)| { + if direct_call { + Instance::resolve(fn_def, args) + } else { + Instance::resolve_for_fn_ptr(fn_def, args) + } + .ok() + }) } fn def_id_to_inst(tcx: TyCtxt<'_>, id: stable_mir::DefId) -> Instance { - let internal_id = rustc_internal::internal(tcx,id); - let internal_inst = rustc_middle::ty::Instance::mono(tcx, internal_id); - rustc_internal::stable(internal_inst) + let internal_id = rustc_internal::internal(tcx, id); + let internal_inst = rustc_middle::ty::Instance::mono(tcx, internal_id); + rustc_internal::stable(internal_inst) } -fn take_any(map: &mut HashMap) -> Option<(K,V)> { - let key = map.keys().next()?.clone(); - map.remove(&key).map(|val| (key,val)) +fn take_any( + map: &mut HashMap, +) -> Option<(K, V)> { + let key = map.keys().next()?.clone(); + map.remove(&key).map(|val| (key, val)) } fn hash(obj: T) -> u64 { @@ -228,17 +285,17 @@ fn hash(obj: T) -> u64 { #[derive(Serialize, Clone)] pub enum MonoItemKind { MonoItemFn { - name: String, - id: stable_mir::DefId, - body: Vec, + name: String, + id: stable_mir::DefId, + body: Vec, }, MonoItemStatic { - name: String, - id: stable_mir::DefId, - allocation: Option, + name: String, + id: stable_mir::DefId, + allocation: Option, }, MonoItemGlobalAsm { - asm: String, + asm: String, }, } #[derive(Serialize, Clone)] @@ -251,49 +308,55 @@ pub struct Item { } fn mk_item(tcx: TyCtxt<'_>, item: MonoItem, sym_name: String) -> Item { - match item { - MonoItem::Fn(inst) => { - let id = inst.def.def_id(); - let name = inst.name(); - let internal_id = rustc_internal::internal(tcx,id); - Item { - mono_item: item, - symbol_name: sym_name, - mono_item_kind: MonoItemKind::MonoItemFn { - name: name.clone(), - id: id, - body: get_bodies(tcx, &inst), - }, - details: get_item_details(tcx, internal_id, Some(inst)) - } - }, - MonoItem::Static(static_def) => { - let internal_id = rustc_internal::internal(tcx,static_def.def_id()); - let alloc = match static_def.eval_initializer() { - Ok(alloc) => Some(alloc), - err => { println!("StaticDef({:#?}).eval_initializer() failed with: {:#?}", static_def, err); None } - }; - Item { - mono_item: item, - symbol_name: sym_name, - mono_item_kind: MonoItemKind::MonoItemStatic { - name: static_def.name(), - id: static_def.def_id(), - allocation: alloc, - }, - details: get_item_details(tcx, internal_id, None), - } - }, - MonoItem::GlobalAsm(ref asm) => { - let asm = format!("{:#?}", asm); - Item { - mono_item: item, - symbol_name: sym_name, - mono_item_kind: MonoItemKind::MonoItemGlobalAsm { asm }, - details: None, - } + match item { + MonoItem::Fn(inst) => { + let id = inst.def.def_id(); + let name = inst.name(); + let internal_id = rustc_internal::internal(tcx, id); + Item { + mono_item: item, + symbol_name: sym_name, + mono_item_kind: MonoItemKind::MonoItemFn { + name: name.clone(), + id, + body: get_bodies(tcx, &inst), + }, + details: get_item_details(tcx, internal_id, Some(inst)), + } + } + MonoItem::Static(static_def) => { + let internal_id = rustc_internal::internal(tcx, static_def.def_id()); + let alloc = match static_def.eval_initializer() { + Ok(alloc) => Some(alloc), + err => { + println!( + "StaticDef({:#?}).eval_initializer() failed with: {:#?}", + static_def, err + ); + None + } + }; + Item { + mono_item: item, + symbol_name: sym_name, + mono_item_kind: MonoItemKind::MonoItemStatic { + name: static_def.name(), + id: static_def.def_id(), + allocation: alloc, + }, + details: get_item_details(tcx, internal_id, None), + } + } + MonoItem::GlobalAsm(ref asm) => { + let asm = format!("{:#?}", asm); + Item { + mono_item: item, + symbol_name: sym_name, + mono_item_kind: MonoItemKind::MonoItemGlobalAsm { asm }, + details: None, + } + } } - } } // Link-time resolution logic @@ -306,47 +369,58 @@ pub enum FnSymType { NormalSym(String), } -type FnSymInfo<'tcx> = (stable_mir::ty::Ty, middle::ty::InstanceKind<'tcx>, FnSymType); - -fn fn_inst_sym<'tcx>(tcx: TyCtxt<'tcx>, ty: Option, inst: Option<&Instance>) -> Option> { - use FnSymType::*; - inst.map(|inst| { - let ty = if let Some(ty) = ty { ty } else { inst.ty() }; - let kind = ty.kind(); - if kind.fn_def().is_some() { - let internal_inst = rustc_internal::internal(tcx, inst); - let sym_type = if inst.is_empty_shim() { - NoOpSym(String::from("")) - } else if let Some(intrinsic_name) = inst.intrinsic_name() { - IntrinsicSym(intrinsic_name) - } else { - NormalSym(inst.mangled_name()) - }; - Some((ty, internal_inst.def, sym_type)) - } else { - None - } - }).flatten() +type FnSymInfo<'tcx> = ( + stable_mir::ty::Ty, + middle::ty::InstanceKind<'tcx>, + FnSymType, +); + +fn fn_inst_sym<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Option, + inst: Option<&Instance>, +) -> Option> { + use FnSymType::*; + inst.and_then(|inst| { + let ty = if let Some(ty) = ty { ty } else { inst.ty() }; + let kind = ty.kind(); + if kind.fn_def().is_some() { + let internal_inst = rustc_internal::internal(tcx, inst); + let sym_type = if inst.is_empty_shim() { + NoOpSym(String::from("")) + } else if let Some(intrinsic_name) = inst.intrinsic_name() { + IntrinsicSym(intrinsic_name) + } else { + NormalSym(inst.mangled_name()) + }; + Some((ty, internal_inst.def, sym_type)) + } else { + None + } + }) } #[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct LinkMapKey<'tcx>(pub stable_mir::ty::Ty, Option>); +pub struct LinkMapKey<'tcx>( + pub stable_mir::ty::Ty, + Option>, +); impl Serialize for LinkMapKey<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - use serde::ser::SerializeTuple; - if link_instance_enabled() { - let mut tup = serializer.serialize_tuple(2)?; - tup.serialize_element(&self.0)?; - tup.serialize_element(&format!("{:?}", self.1).as_str())?; - tup.end() - } else { - ::serialize(&self.0, serializer) + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeTuple; + if link_instance_enabled() { + let mut tup = serializer.serialize_tuple(2)?; + tup.serialize_element(&self.0)?; + tup.serialize_element(&format!("{:?}", self.1).as_str())?; + tup.end() + } else { + ::serialize(&self.0, serializer) + } } - } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] @@ -356,23 +430,32 @@ const TERM: u8 = 1 << 1; const FPTR: u8 = 1 << 2; impl Serialize for ItemSource { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - use serde::ser::SerializeSeq; - let mut seq = serializer.serialize_seq(None)?; - if self.0 & ITEM != 0u8 { seq.serialize_element(&"Item")? }; - if self.0 & TERM != 0u8 { seq.serialize_element(&"Term")? }; - if self.0 & FPTR != 0u8 { seq.serialize_element(&"Fptr")? }; - seq.end() - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeSeq; + let mut seq = serializer.serialize_seq(None)?; + if self.0 & ITEM != 0u8 { + seq.serialize_element(&"Item")? + }; + if self.0 & TERM != 0u8 { + seq.serialize_element(&"Term")? + }; + if self.0 & FPTR != 0u8 { + seq.serialize_element(&"Fptr")? + }; + seq.end() + } } #[derive(Serialize)] pub enum AllocInfo { Function(stable_mir::mir::mono::Instance), - VTable(stable_mir::ty::Ty, Option>), + VTable( + stable_mir::ty::Ty, + Option>, + ), Static(stable_mir::mir::mono::StaticDef), Memory(stable_mir::ty::Allocation), // TODO include stable_mir::ty::TyKind? } @@ -381,77 +464,121 @@ type AllocMap = HashMap; type TyMap = HashMap)>; struct InternedValueCollector<'tcx, 'local> { - tcx: TyCtxt<'tcx>, - _sym: String, - locals: &'local [LocalDecl], - link_map: &'local mut LinkMap<'tcx>, - visited_allocs: &'local mut AllocMap, - visited_tys: &'local mut TyMap, + tcx: TyCtxt<'tcx>, + _sym: String, + locals: &'local [LocalDecl], + link_map: &'local mut LinkMap<'tcx>, + visited_allocs: &'local mut AllocMap, + visited_tys: &'local mut TyMap, } type InternedValues<'tcx> = (LinkMap<'tcx>, AllocMap, TyMap); -fn update_link_map<'tcx>(link_map: &mut LinkMap<'tcx>, fn_sym: Option>, source: ItemSource) { - if fn_sym.is_none() { return } - let (ty, kind, name) = fn_sym.unwrap(); - let new_val = (source, name); - let key = if link_instance_enabled() { LinkMapKey(ty, Some(kind)) } else { LinkMapKey(ty, None) }; - if let Some(curr_val) = link_map.get_mut(&key.clone()) { - if curr_val.1 != new_val.1 { - panic!("Added inconsistent entries into link map! {:?} -> {:?}, {:?}", (ty, ty.kind().fn_def(), &kind), curr_val.1, new_val.1); - } - curr_val.0.0 |= new_val.0.0; - if debug_enabled() { - println!("Regenerated link map entry: {:?}:{:?} -> {:?}", &key, key.0.kind().fn_def(), new_val); +fn update_link_map<'tcx>( + link_map: &mut LinkMap<'tcx>, + fn_sym: Option>, + source: ItemSource, +) { + if fn_sym.is_none() { + return; } - } else { - if debug_enabled() { - println!("Generated link map entry from call: {:?}:{:?} -> {:?}", &key, key.0.kind().fn_def(), new_val); + let (ty, kind, name) = fn_sym.unwrap(); + let new_val = (source, name); + let key = if link_instance_enabled() { + LinkMapKey(ty, Some(kind)) + } else { + LinkMapKey(ty, None) + }; + if let Some(curr_val) = link_map.get_mut(&key.clone()) { + if curr_val.1 != new_val.1 { + panic!( + "Added inconsistent entries into link map! {:?} -> {:?}, {:?}", + (ty, ty.kind().fn_def(), &kind), + curr_val.1, + new_val.1 + ); + } + curr_val.0 .0 |= new_val.0 .0; + if debug_enabled() { + println!( + "Regenerated link map entry: {:?}:{:?} -> {:?}", + &key, + key.0.kind().fn_def(), + new_val + ); + } + } else { + if debug_enabled() { + println!( + "Generated link map entry from call: {:?}:{:?} -> {:?}", + &key, + key.0.kind().fn_def(), + new_val + ); + } + link_map.insert(key.clone(), new_val); } - link_map.insert(key.clone(), new_val); - } } fn get_prov_type(maybe_kind: Option) -> Option { - use stable_mir::ty::RigidTy; - // check for pointers - let kind = if let Some(kind) = maybe_kind { kind } else { return None }; - if let Some(ty) = kind.builtin_deref(true) { - return ty.ty.kind().into(); - } - match kind.rigid().expect("Non-rigid-ty allocation found!") { - RigidTy::Array(ty, _) | RigidTy::Slice(ty) => ty.kind().into(), - RigidTy::FnPtr(_) => None, - _ => todo!(), - } -} - -fn collect_alloc(val_collector: &mut InternedValueCollector, kind: Option, val: stable_mir::mir::alloc::AllocId) { + use stable_mir::ty::RigidTy; + // check for pointers + let kind = maybe_kind?; + if let Some(ty) = kind.builtin_deref(true) { + return ty.ty.kind().into(); + } + match kind.rigid().expect("Non-rigid-ty allocation found!") { + RigidTy::Array(ty, _) | RigidTy::Slice(ty) => ty.kind().into(), + RigidTy::FnPtr(_) => None, + _ => todo!(), + } +} + +fn collect_alloc( + val_collector: &mut InternedValueCollector, + kind: Option, + val: stable_mir::mir::alloc::AllocId, +) { use stable_mir::mir::alloc::GlobalAlloc; let entry = val_collector.visited_allocs.entry(val); - if matches!(entry, std::collections::hash_map::Entry::Occupied(_)) { return; } + if matches!(entry, std::collections::hash_map::Entry::Occupied(_)) { + return; + } let global_alloc = GlobalAlloc::from(val); match global_alloc { GlobalAlloc::Memory(ref alloc) => { let pointed_kind = get_prov_type(kind); - if debug_enabled() { println!("DEBUG: called collect_alloc: {:?}:{:?}:{:?}", val, pointed_kind, global_alloc); } + if debug_enabled() { + println!( + "DEBUG: called collect_alloc: {:?}:{:?}:{:?}", + val, pointed_kind, global_alloc + ); + } entry.or_insert(AllocInfo::Memory(alloc.clone())); // TODO: include pointed_kind.clone().unwrap() ? alloc.provenance.ptrs.iter().for_each(|(_, prov)| { collect_alloc(val_collector, pointed_kind.clone(), prov.0); }); } GlobalAlloc::Static(def) => { - assert!(kind.clone().unwrap().builtin_deref(true).is_some(), "Allocated pointer is not a built-in pointer type: {:?}", kind); + assert!( + kind.clone().unwrap().builtin_deref(true).is_some(), + "Allocated pointer is not a built-in pointer type: {:?}", + kind + ); entry.or_insert(AllocInfo::Static(def)); - }, + } GlobalAlloc::VTable(ty, traitref) => { - assert!(kind.clone().unwrap().builtin_deref(true).is_some(), "Allocated pointer is not a built-in pointer type: {:?}", kind); + assert!( + kind.clone().unwrap().builtin_deref(true).is_some(), + "Allocated pointer is not a built-in pointer type: {:?}", + kind + ); entry.or_insert(AllocInfo::VTable(ty, traitref)); - }, + } GlobalAlloc::Function(inst) => { assert!(kind.unwrap().is_fn_ptr()); entry.or_insert(AllocInfo::Function(inst)); - }, + } }; } @@ -470,37 +597,41 @@ fn collect_arg_tys(collector: &mut InternedValueCollector, args: &stable_mir::ty }, _ => {} } - } + } } fn collect_ty(val_collector: &mut InternedValueCollector, val: stable_mir::ty::Ty) { - use stable_mir::ty::{RigidTy::*, TyKind::RigidTy, AdtDef}; + use stable_mir::ty::{AdtDef, RigidTy::*, TyKind::RigidTy}; // HACK: std::fmt::Arguments has escaping bounds and will error if trying to get the layout. // We will just ban producing the layout for now see, this issue for more info // https://github.com/runtimeverification/smir_pretty/issues/27 let maybe_layout = match val.kind() { - stable_mir::ty::TyKind::RigidTy(Adt(AdtDef(def_id_stable), _)) => { - let def_id_internal = rustc_internal::internal(val_collector.tcx, def_id_stable); - let name = rustc_middle::ty::print::with_no_trimmed_paths!(val_collector.tcx.def_path_str(def_id_internal)); - if String::from("std::fmt::Arguments") == name { - None - } else { - Some(val.layout()) + stable_mir::ty::TyKind::RigidTy(Adt(AdtDef(def_id_stable), _)) => { + let def_id_internal = rustc_internal::internal(val_collector.tcx, def_id_stable); + let name = rustc_middle::ty::print::with_no_trimmed_paths!(val_collector + .tcx + .def_path_str(def_id_internal)); + if *"std::fmt::Arguments" == name { + None + } else { + Some(val.layout()) + } } - }, - _ => { - Some(val.layout()) - } + _ => Some(val.layout()), }; let maybe_layout_shape = if let Some(Ok(layout)) = maybe_layout { - Some(layout.shape()) + Some(layout.shape()) } else { - None + None }; - if val_collector.visited_tys.insert(hash(val), (val.kind(), maybe_layout_shape)).is_some() { + if val_collector + .visited_tys + .insert(hash(val), (val.kind(), maybe_layout_shape)) + .is_some() + { match val.kind() { RigidTy(Array(ty, _) | Pat(ty, _) | Slice(ty) | RawPtr(ty, _) | Ref(_, ty, _)) => { collect_ty(val_collector, ty) @@ -515,293 +646,369 @@ fn collect_ty(val_collector: &mut InternedValueCollector, val: stable_mir::ty::T collect_arg_tys(val_collector, args); } // FIXME: Would be good to grab the coroutine signature - RigidTy(Coroutine(_, ref args, _) | CoroutineWitness(_, ref args)) => collect_arg_tys(val_collector, args), + RigidTy(Coroutine(_, ref args, _) | CoroutineWitness(_, ref args)) => { + collect_arg_tys(val_collector, args) + } ref kind @ RigidTy(FnDef(_, ref args) | Closure(_, ref args)) => { - collect_vec_tys(val_collector, kind.fn_sig().unwrap().value.inputs_and_output); + collect_vec_tys( + val_collector, + kind.fn_sig().unwrap().value.inputs_and_output, + ); collect_arg_tys(val_collector, args); } RigidTy(Foreign(def)) => match def.kind() { - ForeignItemKind::Fn(def) => collect_vec_tys(val_collector, def.fn_sig().value.inputs_and_output), + ForeignItemKind::Fn(def) => { + collect_vec_tys(val_collector, def.fn_sig().value.inputs_and_output) + } ForeignItemKind::Type(ty) => collect_ty(val_collector, ty), ForeignItemKind::Static(def) => collect_ty(val_collector, def.ty()), - } + }, _ => {} } } } impl MirVisitor for InternedValueCollector<'_, '_> { - fn visit_terminator(&mut self, term: &Terminator, loc: stable_mir::mir::visit::Location) { - use TerminatorKind::*; - use stable_mir::mir::{Operand::Constant, ConstOperand}; - let fn_sym = match &term.kind { - Call { func: Constant(ConstOperand { const_: cnst, .. }), args: _, .. } => { - if *cnst.kind() != stable_mir::ty::ConstantKind::ZeroSized { return } - let inst = fn_inst_for_ty(cnst.ty(), true).expect("Direct calls to functions must resolve to an instance"); - fn_inst_sym(self.tcx, Some(cnst.ty()), Some(&inst)) - } - Drop { place, .. } => { - let drop_ty = place.ty(self.locals).unwrap(); - let inst = Instance::resolve_drop_in_place(drop_ty); - fn_inst_sym(self.tcx, None, Some(&inst)) - } - _ => None - }; - update_link_map(self.link_map, fn_sym, ItemSource(TERM)); - self.super_terminator(term, loc); - } - - fn visit_rvalue(&mut self, rval: &Rvalue, loc: stable_mir::mir::visit::Location) { - use stable_mir::mir::{PointerCoercion, CastKind}; - match rval { - Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer), ref op, _) => { - let inst = fn_inst_for_ty(op.ty(self.locals).unwrap(), false).expect("ReifyFnPointer Cast operand type does not resolve to an instance"); - let fn_sym = fn_inst_sym(self.tcx, None, Some(&inst)); - update_link_map(self.link_map, fn_sym, ItemSource(FPTR)); - } - _ => {} - }; - self.super_rvalue(rval, loc); - } - - fn visit_mir_const(&mut self, constant: &stable_mir::ty::MirConst, loc: stable_mir::mir::visit::Location) { - use stable_mir::ty::{ConstantKind, TyConstKind}; // TyConst - match constant.kind() { - ConstantKind::Allocated(alloc) => { - if debug_enabled() { println!("visited_mir_const::Allocated({:?}) as {:?}", alloc, constant.ty().kind()); } - alloc.provenance.ptrs.iter().for_each(|(_offset, prov)| collect_alloc(self, Some(constant.ty().kind()), prov.0)); - }, - ConstantKind::Ty(ty_const) => { - if let TyConstKind::Value(..) = ty_const.kind() { - panic!("TyConstKind::Value"); + fn visit_terminator(&mut self, term: &Terminator, loc: stable_mir::mir::visit::Location) { + use stable_mir::mir::{ConstOperand, Operand::Constant}; + use TerminatorKind::*; + let fn_sym = match &term.kind { + Call { + func: Constant(ConstOperand { const_: cnst, .. }), + args: _, + .. + } => { + if *cnst.kind() != stable_mir::ty::ConstantKind::ZeroSized { + return; + } + let inst = fn_inst_for_ty(cnst.ty(), true) + .expect("Direct calls to functions must resolve to an instance"); + fn_inst_sym(self.tcx, Some(cnst.ty()), Some(&inst)) + } + Drop { place, .. } => { + let drop_ty = place.ty(self.locals).unwrap(); + let inst = Instance::resolve_drop_in_place(drop_ty); + fn_inst_sym(self.tcx, None, Some(&inst)) + } + _ => None, + }; + update_link_map(self.link_map, fn_sym, ItemSource(TERM)); + self.super_terminator(term, loc); + } + + fn visit_rvalue(&mut self, rval: &Rvalue, loc: stable_mir::mir::visit::Location) { + use stable_mir::mir::{CastKind, PointerCoercion}; + + #[allow(clippy::single_match)] // TODO: Unsure if we need to fill these out + match rval { + Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer), ref op, _) => { + let inst = fn_inst_for_ty(op.ty(self.locals).unwrap(), false) + .expect("ReifyFnPointer Cast operand type does not resolve to an instance"); + let fn_sym = fn_inst_sym(self.tcx, None, Some(&inst)); + update_link_map(self.link_map, fn_sym, ItemSource(FPTR)); + } + _ => {} + }; + self.super_rvalue(rval, loc); + } + + fn visit_mir_const( + &mut self, + constant: &stable_mir::ty::MirConst, + loc: stable_mir::mir::visit::Location, + ) { + use stable_mir::ty::{ConstantKind, TyConstKind}; // TyConst + match constant.kind() { + ConstantKind::Allocated(alloc) => { + if debug_enabled() { + println!( + "visited_mir_const::Allocated({:?}) as {:?}", + alloc, + constant.ty().kind() + ); + } + alloc.provenance.ptrs.iter().for_each(|(_offset, prov)| { + collect_alloc(self, Some(constant.ty().kind()), prov.0) + }); + } + ConstantKind::Ty(ty_const) => { + if let TyConstKind::Value(..) = ty_const.kind() { + panic!("TyConstKind::Value"); + } + } + ConstantKind::Unevaluated(_) | ConstantKind::Param(_) | ConstantKind::ZeroSized => {} } - }, - ConstantKind::Unevaluated(_) | ConstantKind::Param(_) | ConstantKind::ZeroSized => {} + self.super_mir_const(constant, loc); } - self.super_mir_const(constant, loc); - } - fn visit_ty(&mut self, ty: &stable_mir::ty::Ty, _location: stable_mir::mir::visit::Location) { - collect_ty(self, *ty); - } + fn visit_ty(&mut self, ty: &stable_mir::ty::Ty, _location: stable_mir::mir::visit::Location) { + collect_ty(self, *ty); + } } -fn collect_interned_values<'tcx,'local>(tcx: TyCtxt<'tcx>, items: Vec<&'local MonoItem>) -> InternedValues<'tcx> { - let mut calls_map = HashMap::new(); - let mut visited_tys = HashMap::new(); - let mut visited_allocs = HashMap::new(); - if link_items_enabled() { - for item in items.iter() { - if let MonoItem::Fn ( inst ) = item { - update_link_map(&mut calls_map, fn_inst_sym(tcx, None, Some(inst)), ItemSource(ITEM)) - } +fn collect_interned_values<'tcx>(tcx: TyCtxt<'tcx>, items: Vec<&MonoItem>) -> InternedValues<'tcx> { + let mut calls_map = HashMap::new(); + let mut visited_tys = HashMap::new(); + let mut visited_allocs = HashMap::new(); + if link_items_enabled() { + for item in items.iter() { + if let MonoItem::Fn(inst) = item { + update_link_map( + &mut calls_map, + fn_inst_sym(tcx, None, Some(inst)), + ItemSource(ITEM), + ) + } + } } - } - for item in items.iter() { - match &item { - MonoItem::Fn( inst ) => { - for body in get_bodies(tcx, inst).into_iter() { - InternedValueCollector { - tcx, - _sym: inst.mangled_name(), - locals: body.locals(), - link_map: &mut calls_map, - visited_tys: &mut visited_tys, - visited_allocs: &mut visited_allocs, - }.visit_body(&body) - } - } - MonoItem::Static(def) => { - let inst = def_id_to_inst(tcx, def.def_id()); - for body in get_bodies(tcx, &inst).into_iter() { - InternedValueCollector { - tcx, - _sym: inst.mangled_name(), - locals: &[], - link_map: &mut calls_map, - visited_tys: &mut visited_tys, - visited_allocs: &mut visited_allocs, - }.visit_body(&body) - } - } - MonoItem::GlobalAsm(_) => {} + for item in items.iter() { + match &item { + MonoItem::Fn(inst) => { + for body in get_bodies(tcx, inst).into_iter() { + InternedValueCollector { + tcx, + _sym: inst.mangled_name(), + locals: body.locals(), + link_map: &mut calls_map, + visited_tys: &mut visited_tys, + visited_allocs: &mut visited_allocs, + } + .visit_body(&body) + } + } + MonoItem::Static(def) => { + let inst = def_id_to_inst(tcx, def.def_id()); + for body in get_bodies(tcx, &inst).into_iter() { + InternedValueCollector { + tcx, + _sym: inst.mangled_name(), + locals: &[], + link_map: &mut calls_map, + visited_tys: &mut visited_tys, + visited_allocs: &mut visited_allocs, + } + .visit_body(&body) + } + } + MonoItem::GlobalAsm(_) => {} + } } - } - (calls_map, visited_allocs, visited_tys) + (calls_map, visited_allocs, visited_tys) } - // Collection Transitive Closure // ============================= struct UnevaluatedConstCollector<'tcx, 'local> { - tcx: TyCtxt<'tcx>, - unevaluated_consts: &'local mut HashMap, - processed_items: &'local mut HashMap, - pending_items: &'local mut HashMap, - current_item: u64, -} - -impl MirVisitor for UnevaluatedConstCollector<'_,'_> { - fn visit_mir_const(&mut self, constant: &stable_mir::ty::MirConst, _location: stable_mir::mir::visit::Location) { - if let stable_mir::ty::ConstantKind::Unevaluated(uconst) = constant.kind() { - let internal_def = rustc_internal::internal(self.tcx, uconst.def.def_id()); - let internal_args = rustc_internal::internal(self.tcx, uconst.args.clone()); - let maybe_inst = rustc_middle::ty::Instance::try_resolve(self.tcx, TypingEnv::post_analysis(self.tcx, internal_def), internal_def, internal_args); - let inst = maybe_inst.ok().flatten().expect(format!("Failed to resolve mono item for {:?}", uconst).as_str()); - let internal_mono_item = rustc_middle::mir::mono::MonoItem::Fn(inst); - let item_name = mono_item_name_int(self.tcx, &internal_mono_item); - if ! ( self.processed_items.contains_key(&item_name) - || self.pending_items.contains_key(&item_name) - || self.current_item == hash(&item_name) - ) - { - if debug_enabled() { println!("Adding unevaluated const body for: {}", item_name); } - self.unevaluated_consts.insert(uconst.def, item_name.clone()); - self.pending_items.insert(item_name.clone(), mk_item(self.tcx, rustc_internal::stable(internal_mono_item), item_name)); - } + tcx: TyCtxt<'tcx>, + unevaluated_consts: &'local mut HashMap, + processed_items: &'local mut HashMap, + pending_items: &'local mut HashMap, + current_item: u64, +} + +impl MirVisitor for UnevaluatedConstCollector<'_, '_> { + fn visit_mir_const( + &mut self, + constant: &stable_mir::ty::MirConst, + _location: stable_mir::mir::visit::Location, + ) { + if let stable_mir::ty::ConstantKind::Unevaluated(uconst) = constant.kind() { + let internal_def = rustc_internal::internal(self.tcx, uconst.def.def_id()); + let internal_args = rustc_internal::internal(self.tcx, uconst.args.clone()); + let maybe_inst = rustc_middle::ty::Instance::try_resolve( + self.tcx, + TypingEnv::post_analysis(self.tcx, internal_def), + internal_def, + internal_args, + ); + let inst = maybe_inst + .ok() + .flatten() + .unwrap_or_else(|| panic!("Failed to resolve mono item for {:?}", uconst)); + let internal_mono_item = rustc_middle::mir::mono::MonoItem::Fn(inst); + let item_name = mono_item_name_int(self.tcx, &internal_mono_item); + if !(self.processed_items.contains_key(&item_name) + || self.pending_items.contains_key(&item_name) + || self.current_item == hash(&item_name)) + { + if debug_enabled() { + println!("Adding unevaluated const body for: {}", item_name); + } + self.unevaluated_consts + .insert(uconst.def, item_name.clone()); + self.pending_items.insert( + item_name.clone(), + mk_item( + self.tcx, + rustc_internal::stable(internal_mono_item), + item_name, + ), + ); + } + } } - } -} - -fn collect_unevaluated_constant_items(tcx: TyCtxt<'_>, items: HashMap) -> (HashMap, Vec) { - // setup collector prerequisites - let mut unevaluated_consts = HashMap::new(); - let mut processed_items = HashMap::new(); - let mut pending_items = items; - loop { - // get next pending item - let (curr_name, value) = if let Some(v) = take_any(&mut pending_items) { v } else { break }; - - // skip item if it isn't a function - let bodies = match value.mono_item_kind { - MonoItemKind::MonoItemFn { ref body, .. } => body, - _ => continue - }; - - // create new collector for function body - let mut collector = UnevaluatedConstCollector { - tcx, - unevaluated_consts: &mut unevaluated_consts, - processed_items: &mut processed_items, - pending_items: &mut pending_items, - current_item: hash(&curr_name), - }; - - // add each fresh collected constant to pending new items - bodies.iter().for_each(|body| collector.visit_body(body)); +} - // move processed item into seen items - processed_items.insert(curr_name.to_string(), value); - } +fn collect_unevaluated_constant_items( + tcx: TyCtxt<'_>, + items: HashMap, +) -> (HashMap, Vec) { + // setup collector prerequisites + let mut unevaluated_consts = HashMap::new(); + let mut processed_items = HashMap::new(); + let mut pending_items = items; + + while let Some((curr_name, value)) = take_any(&mut pending_items) { + // skip item if it isn't a function + let bodies = match value.mono_item_kind { + MonoItemKind::MonoItemFn { ref body, .. } => body, + _ => continue, + }; + + // create new collector for function body + let mut collector = UnevaluatedConstCollector { + tcx, + unevaluated_consts: &mut unevaluated_consts, + processed_items: &mut processed_items, + pending_items: &mut pending_items, + current_item: hash(&curr_name), + }; + + // add each fresh collected constant to pending new items + bodies.iter().for_each(|body| collector.visit_body(body)); + + // move processed item into seen items + processed_items.insert(curr_name.to_string(), value); + } - (unevaluated_consts, processed_items.drain().map(|(_name,item)| item).collect()) + ( + unevaluated_consts, + processed_items.drain().map(|(_name, item)| item).collect(), + ) } // Core item collection logic // ========================== fn mono_collect(tcx: TyCtxt<'_>) -> Vec { - let units = tcx.collect_and_partition_mono_items(()).1; - units.iter().flat_map(|unit| { - unit.items_in_deterministic_order(tcx).iter().map(|(internal_item, _)| rustc_internal::stable(internal_item)).collect::>() - }).collect() + let units = tcx.collect_and_partition_mono_items(()).1; + units + .iter() + .flat_map(|unit| { + unit.items_in_deterministic_order(tcx) + .iter() + .map(|(internal_item, _)| rustc_internal::stable(internal_item)) + .collect::>() + }) + .collect() } fn collect_items(tcx: TyCtxt<'_>) -> HashMap { - // get initial set of mono_items - let items = mono_collect(tcx); - items.iter().map(|item| { - let name = mono_item_name(tcx, item); - ( name.clone(), mk_item(tcx, item.clone(), name) ) - }).collect::>() + // get initial set of mono_items + let items = mono_collect(tcx); + items + .iter() + .map(|item| { + let name = mono_item_name(tcx, item); + (name.clone(), mk_item(tcx, item.clone(), name)) + }) + .collect::>() } /// the serialised data structure as a whole #[derive(Serialize)] pub struct SmirJson<'t> { - pub name: String, - pub crate_id: u64, - pub allocs: Vec<(AllocId,AllocInfo)>, - pub functions: Vec<(LinkMapKey<'t>, FnSymType)>, - pub uneval_consts: Vec<(ConstDef, String)>, - pub items: Vec, - pub debug: Option> + pub name: String, + pub crate_id: u64, + pub allocs: Vec<(AllocId, AllocInfo)>, + pub functions: Vec<(LinkMapKey<'t>, FnSymType)>, + pub uneval_consts: Vec<(ConstDef, String)>, + pub items: Vec, + pub debug: Option>, } #[derive(Serialize)] pub struct SmirJsonDebugInfo<'t> { - fn_sources: Vec<(LinkMapKey<'t>,ItemSource)>, - types: TyMap, - foreign_modules: Vec<(String, Vec)> + fn_sources: Vec<(LinkMapKey<'t>, ItemSource)>, + types: TyMap, + foreign_modules: Vec<(String, Vec)>, } // Serialization Entrypoint // ======================== pub fn collect_smir(tcx: TyCtxt<'_>) -> SmirJson { - let local_crate = stable_mir::local_crate(); - let items = collect_items(tcx); - let items_clone = items.clone(); - let (unevaluated_consts, mut items) = - collect_unevaluated_constant_items(tcx, items); - let (calls_map, visited_allocs, visited_tys) = - collect_interned_values(tcx, items.iter().map(|i| &i.mono_item).collect::>()); - - // FIXME: We dump extra static items here --- this should be handled better - for (_, alloc) in visited_allocs.iter() { - if let AllocInfo::Static(def) = alloc { - let mono_item = stable_mir::mir::mono::MonoItem::Fn(stable_mir::mir::mono::Instance::from(*def)); - let item_name = &mono_item_name(tcx, &mono_item); - if !items_clone.contains_key(item_name) { - println!("Items missing static with id {:?} and name {:?}", def, item_name); - items.push(mk_item(tcx, mono_item, item_name.clone())); - } - } - } - - let debug: Option = - if debug_enabled() { - let fn_sources = - calls_map.clone().into_iter().map(|(k,(source,_))| (k,source)).collect::>(); - Some(SmirJsonDebugInfo { fn_sources, types: visited_tys, foreign_modules: get_foreign_module_details()}) + let local_crate = stable_mir::local_crate(); + let items = collect_items(tcx); + let items_clone = items.clone(); + let (unevaluated_consts, mut items) = collect_unevaluated_constant_items(tcx, items); + let (calls_map, visited_allocs, visited_tys) = + collect_interned_values(tcx, items.iter().map(|i| &i.mono_item).collect::>()); + + // FIXME: We dump extra static items here --- this should be handled better + for (_, alloc) in visited_allocs.iter() { + if let AllocInfo::Static(def) = alloc { + let mono_item = + stable_mir::mir::mono::MonoItem::Fn(stable_mir::mir::mono::Instance::from(*def)); + let item_name = &mono_item_name(tcx, &mono_item); + if !items_clone.contains_key(item_name) { + println!( + "Items missing static with id {:?} and name {:?}", + def, item_name + ); + items.push(mk_item(tcx, mono_item, item_name.clone())); + } + } + } + + let debug: Option = if debug_enabled() { + let fn_sources = calls_map + .clone() + .into_iter() + .map(|(k, (source, _))| (k, source)) + .collect::>(); + Some(SmirJsonDebugInfo { + fn_sources, + types: visited_tys, + foreign_modules: get_foreign_module_details(), + }) } else { - None + None }; - let called_functions = - calls_map.into_iter().map(|(k,(_,name))| (k,name)).collect::>(); - let allocs = - visited_allocs.into_iter().collect::>(); - let crate_id = - tcx.stable_crate_id(LOCAL_CRATE).as_u64(); - - SmirJson { - name: local_crate.name, - crate_id: crate_id, - allocs, - functions: called_functions, - uneval_consts: unevaluated_consts.into_iter().collect(), - items, - debug - } - + let called_functions = calls_map + .into_iter() + .map(|(k, (_, name))| (k, name)) + .collect::>(); + let allocs = visited_allocs.into_iter().collect::>(); + let crate_id = tcx.stable_crate_id(LOCAL_CRATE).as_u64(); + + SmirJson { + name: local_crate.name, + crate_id, + allocs, + functions: called_functions, + uneval_consts: unevaluated_consts.into_iter().collect(), + items, + debug, + } } pub fn emit_smir(tcx: TyCtxt<'_>) { + let smir_json = + serde_json::to_string(&collect_smir(tcx)).expect("serde_json failed to write result"); - let smir_json = serde_json::to_string(&collect_smir(tcx)).expect("serde_json failed to write result"); - - match tcx.output_filenames(()).path(OutputType::Mir) { - OutFileName::Stdout => { - write!(&io::stdout(), "{}", smir_json).expect("Failed to write smir.json"); - } - OutFileName::Real(path) => { - let mut b = - io::BufWriter::new( - File::create(&path.with_extension("smir.json")) - .expect("Failed to create {path}.smir.json output file")); - write!(b, "{}", smir_json).expect("Failed to write smir.json"); + match tcx.output_filenames(()).path(OutputType::Mir) { + OutFileName::Stdout => { + write!(&io::stdout(), "{}", smir_json).expect("Failed to write smir.json"); + } + OutFileName::Real(path) => { + let mut b = io::BufWriter::new( + File::create(path.with_extension("smir.json")) + .expect("Failed to create {path}.smir.json output file"), + ); + write!(b, "{}", smir_json).expect("Failed to write smir.json"); + } } - } } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 3335cd9..43aa115 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -7,9 +7,15 @@ pub fn get_resource_path(components: Vec<&str>) -> String { pathbuf.push(component); } let path = pathbuf.as_path(); - if ! path.exists() { - panic!("Test resource file is not found or not readable: {}", path.display()); + if !path.exists() { + panic!( + "Test resource file is not found or not readable: {}", + path.display() + ); } - return path.to_str().expect("test path was not a valid string").into(); + return path + .to_str() + .expect("test path was not a valid string") + .into(); } diff --git a/tests/test_basic.rs b/tests/test_basic.rs index e86c88d..a0c6a87 100644 --- a/tests/test_basic.rs +++ b/tests/test_basic.rs @@ -1,8 +1,14 @@ mod common; use common::*; -use smir_pretty::{stable_mir_driver, print_all_items_verbose}; +use smir_pretty::{print_all_items_verbose, stable_mir_driver}; #[test] fn test_pretty_print() { - stable_mir_driver(& vec!["rustc".into(), get_resource_path(vec!["tests", "resources", "println.rs"])], print_all_items_verbose); + stable_mir_driver( + &vec![ + "rustc".into(), + get_resource_path(vec!["tests", "resources", "println.rs"]), + ], + print_all_items_verbose, + ); }