1
Fork 0

Incorporate jyn's feedback

* Move call location logic from function constructor to rendering
* Fix issue with macro spans in scraping examples
* Clean up example loading logic

Documentation / newtype for DecorationInfo

Fix line number display

Serialize edition of call site, other small cleanup
This commit is contained in:
Will Crichton 2021-09-16 18:12:45 -07:00
parent a1cb19444f
commit 829b1a9dd9
12 changed files with 181 additions and 130 deletions

View file

@ -235,7 +235,7 @@ fn build_external_function(cx: &mut DocContext<'_>, did: DefId) -> clean::Functi
decl,
generics,
header: hir::FnHeader { unsafety: sig.unsafety(), abi: sig.abi(), constness, asyncness },
call_locations: None,
def_id: did,
}
}

View file

@ -801,10 +801,8 @@ impl<'a> Clean<Function> for (&'a hir::FnSig<'a>, &'a hir::Generics<'a>, hir::Bo
fn clean(&self, cx: &mut DocContext<'_>) -> Function {
let (generics, decl) =
enter_impl_trait(cx, |cx| (self.1.clean(cx), (&*self.0.decl, self.2).clean(cx)));
let mut function = Function { decl, generics, header: self.0.header, call_locations: None };
let def_id = self.2.hir_id.owner.to_def_id();
function.load_call_locations(def_id, cx);
function
Function { decl, generics, header: self.0.header, def_id }
}
}
@ -936,14 +934,13 @@ impl Clean<Item> for hir::TraitItem<'_> {
let (generics, decl) = enter_impl_trait(cx, |cx| {
(self.generics.clean(cx), (&*sig.decl, &names[..]).clean(cx))
});
let mut t =
Function { header: sig.header, decl, generics, call_locations: None };
let def_id = self.def_id.to_def_id();
let mut t = Function { header: sig.header, decl, generics, def_id };
if t.header.constness == hir::Constness::Const
&& is_unstable_const_fn(cx.tcx, local_did).is_some()
{
t.header.constness = hir::Constness::NotConst;
}
t.load_call_locations(self.def_id.to_def_id(), cx);
TyMethodItem(t)
}
hir::TraitItemKind::Type(ref bounds, ref default) => {
@ -1062,7 +1059,7 @@ impl Clean<Item> for ty::AssocItem {
ty::ImplContainer(_) => Some(self.defaultness),
ty::TraitContainer(_) => None,
};
let mut function = Function {
let function = Function {
generics,
decl,
header: hir::FnHeader {
@ -1071,12 +1068,11 @@ impl Clean<Item> for ty::AssocItem {
constness,
asyncness,
},
call_locations: None,
def_id: self.def_id,
};
function.load_call_locations(self.def_id, cx);
MethodItem(function, defaultness)
} else {
let mut function = Function {
let function = Function {
generics,
decl,
header: hir::FnHeader {
@ -1085,9 +1081,8 @@ impl Clean<Item> for ty::AssocItem {
constness: hir::Constness::NotConst,
asyncness: hir::IsAsync::NotAsync,
},
call_locations: None,
def_id: self.def_id,
};
function.load_call_locations(self.def_id, cx);
TyMethodItem(function)
}
}
@ -2086,7 +2081,8 @@ fn clean_use_statement(
impl Clean<Item> for (&hir::ForeignItem<'_>, Option<Symbol>) {
fn clean(&self, cx: &mut DocContext<'_>) -> Item {
let (item, renamed) = self;
cx.with_param_env(item.def_id.to_def_id(), |cx| {
let def_id = item.def_id.to_def_id();
cx.with_param_env(def_id, |cx| {
let kind = match item.kind {
hir::ForeignItemKind::Fn(ref decl, ref names, ref generics) => {
let abi = cx.tcx.hir().get_foreign_abi(item.hir_id());
@ -2106,7 +2102,7 @@ impl Clean<Item> for (&hir::ForeignItem<'_>, Option<Symbol>) {
constness: hir::Constness::NotConst,
asyncness: hir::IsAsync::NotAsync,
},
call_locations: None,
def_id,
})
}
hir::ForeignItemKind::Static(ref ty, mutability) => {

View file

@ -42,7 +42,6 @@ use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::html::render::cache::ExternalLocation;
use crate::html::render::Context;
use crate::scrape_examples::{self, FnCallLocations};
use self::FnRetTy::*;
use self::ItemKind::*;
@ -1255,19 +1254,7 @@ crate struct Function {
crate decl: FnDecl,
crate generics: Generics,
crate header: hir::FnHeader,
crate call_locations: Option<FnCallLocations>,
}
impl Function {
/// If --scrape-examples is used, then this function attempts to find call locations
/// for `self` within `RenderOptions::call_locations` and store them in `Function::call_locations`.
crate fn load_call_locations(&mut self, def_id: hir::def_id::DefId, cx: &DocContext<'_>) {
if let Some(call_locations) = cx.render_options.call_locations.as_ref() {
let key = scrape_examples::def_id_call_key(cx.tcx, def_id);
self.call_locations = call_locations.get(&key).cloned();
debug!("call_locations: {:?} -- {:?}", key, self.call_locations);
}
}
crate def_id: DefId,
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]

View file

@ -207,6 +207,7 @@ impl fmt::Debug for Options {
.field("run_check", &self.run_check)
.field("no_run", &self.no_run)
.field("nocapture", &self.nocapture)
.field("scrape_examples", &self.scrape_examples)
.finish()
}
}
@ -285,7 +286,7 @@ crate struct RenderOptions {
crate emit: Vec<EmitType>,
/// If `true`, HTML source pages will generate links for items to their definition.
crate generate_link_to_definition: bool,
crate call_locations: Option<AllCallLocations>,
crate call_locations: AllCallLocations,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]

View file

@ -31,7 +31,9 @@ crate struct ContextInfo<'a, 'b, 'c> {
crate root_path: &'c str,
}
crate type DecorationInfo = FxHashMap<&'static str, Vec<(u32, u32)>>;
/// Decorations are represented as a map from CSS class to vector of character ranges.
/// Each range will be wrapped in a span with that class.
crate struct DecorationInfo(crate FxHashMap<&'static str, Vec<(u32, u32)>>);
/// Highlights `src`, returning the HTML output.
crate fn render_with_highlighting(
@ -273,6 +275,7 @@ struct Decorations {
impl Decorations {
fn new(info: DecorationInfo) -> Self {
let (starts, ends) = info
.0
.into_iter()
.map(|(kind, ranges)| ranges.into_iter().map(move |(lo, hi)| ((lo, kind), hi)))
.flatten()
@ -305,6 +308,7 @@ impl<'a> Classifier<'a> {
decoration_info: Option<DecorationInfo>,
) -> Classifier<'_> {
let tokens = PeekIter::new(TokenIter { src });
let decorations = decoration_info.map(Decorations::new);
Classifier {
tokens,
in_attribute: false,

View file

@ -35,6 +35,7 @@ use crate::html::format::Buffer;
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
use crate::html::static_files::PAGE;
use crate::html::{layout, sources};
use crate::scrape_examples::AllCallLocations;
/// Major driving force in all rustdoc rendering. This contains information
/// about where in the tree-like hierarchy rendering is occurring and controls
@ -124,6 +125,8 @@ crate struct SharedContext<'tcx> {
crate span_correspondance_map: FxHashMap<rustc_span::Span, LinkFromSrc>,
/// The [`Cache`] used during rendering.
crate cache: Cache,
crate call_locations: AllCallLocations,
}
impl SharedContext<'_> {
@ -389,6 +392,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
generate_redirect_map,
show_type_layout,
generate_link_to_definition,
call_locations,
..
} = options;
@ -480,6 +484,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
templates,
span_correspondance_map: matches,
cache,
call_locations,
};
// Add the default themes to the `Vec` of stylepaths

View file

@ -73,8 +73,9 @@ use crate::html::format::{
print_generic_bounds, print_where_clause, Buffer, HrefError, PrintWithSpace,
};
use crate::html::markdown::{HeadingOffset, Markdown, MarkdownHtml, MarkdownSummaryLine};
use crate::html::highlight;
use crate::html::sources;
use crate::scrape_examples::{CallData, FnCallLocations};
use crate::scrape_examples::CallData;
/// A pair of name and its optional document.
crate type NameDoc = (String, Option<String>);
@ -592,9 +593,13 @@ fn document_full_inner(
}
}
match &*item.kind {
let kind = match &*item.kind {
clean::ItemKind::StrippedItem(box kind) | kind => kind,
};
match kind {
clean::ItemKind::FunctionItem(f) | clean::ItemKind::MethodItem(f, _) => {
render_call_locations(w, cx, &f.call_locations, item);
render_call_locations(w, cx, f.def_id, item);
}
_ => {}
}
@ -2458,14 +2463,11 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
const MAX_FULL_EXAMPLES: usize = 5;
/// Generates the HTML for example call locations generated via the --scrape-examples flag.
fn render_call_locations(
w: &mut Buffer,
cx: &Context<'_>,
call_locations: &Option<FnCallLocations>,
item: &clean::Item,
) {
let call_locations = match call_locations.as_ref() {
Some(call_locations) if call_locations.len() > 0 => call_locations,
fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, def_id: DefId, item: &clean::Item) {
let tcx = cx.tcx();
let key = crate::scrape_examples::def_id_call_key(tcx, def_id);
let call_locations = match cx.shared.call_locations.get(&key) {
Some(call_locations) => call_locations,
_ => {
return;
}
@ -2483,7 +2485,6 @@ fn render_call_locations(
);
// Generate the HTML for a single example, being the title and code block
let tcx = cx.tcx();
let write_example = |w: &mut Buffer, (path, call_data): (&PathBuf, &CallData)| -> bool {
let contents = match fs::read_to_string(&path) {
Ok(contents) => contents,
@ -2497,40 +2498,51 @@ fn render_call_locations(
// To reduce file sizes, we only want to embed the source code needed to understand the example, not
// the entire file. So we find the smallest byte range that covers all items enclosing examples.
assert!(call_data.locations.len() > 0);
assert!(!call_data.locations.is_empty());
let min_loc =
call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
let min_byte = min_loc.enclosing_item.byte_span.0;
let min_line = min_loc.enclosing_item.line_span.0;
let max_byte =
let (byte_offset, _) = min_loc.enclosing_item.byte_span;
let (line_offset, _) = min_loc.enclosing_item.line_span;
let byte_ceiling =
call_data.locations.iter().map(|loc| loc.enclosing_item.byte_span.1).max().unwrap();
// The output code is limited to that byte range.
let contents_subset = &contents[(min_byte as usize)..(max_byte as usize)];
let contents_subset = &contents[(byte_offset as usize)..(byte_ceiling as usize)];
// The call locations need to be updated to reflect that the size of the program has changed.
// Specifically, the ranges are all subtracted by `min_byte` since that's the new zero point.
// Specifically, the ranges are all subtracted by `byte_offset` since that's the new zero point.
let (byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
.locations
.iter()
.map(|loc| {
let (byte_lo, byte_hi) = loc.call_expr.byte_span;
let (line_lo, line_hi) = loc.call_expr.line_span;
((byte_lo - min_byte, byte_hi - min_byte), (line_lo - min_line, line_hi - min_line))
(
(byte_lo - byte_offset, byte_hi - byte_offset),
(line_lo - line_offset, line_hi - line_offset),
)
})
.unzip();
let edition = cx.shared.edition();
let (init_min, init_max) = line_ranges[0];
let line_range = if init_min == init_max {
format!("line {}", init_min + line_offset + 1)
} else {
format!("lines {}-{}", init_min + line_offset + 1, init_max + line_offset + 1)
};
write!(
w,
r#"<div class="scraped-example" data-locs="{locations}">
r#"<div class="scraped-example" data-locs="{locations}" data-offset="{offset}">
<div class="scraped-example-title">
{name} <a href="{root}{url}" target="_blank">[src]</a>
{name} (<a href="{root}{url}" target="_blank">{line_range}</a>)
</div>
<div class="code-wrapper">"#,
root = cx.root_path(),
url = call_data.url,
name = call_data.display_name,
line_range = line_range,
offset = line_offset,
// The locations are encoded as a data attribute, so they can be read
// later by the JS for interactions.
locations = serde_json::to_string(&line_ranges).unwrap(),
@ -2551,8 +2563,8 @@ fn render_call_locations(
_ => false,
})?;
Some(rustc_span::Span::with_root_ctxt(
file.start_pos + BytePos(min_byte),
file.start_pos + BytePos(max_byte),
file.start_pos + BytePos(byte_offset),
file.start_pos + BytePos(byte_ceiling),
))
})()
.unwrap_or(rustc_span::DUMMY_SP);
@ -2566,12 +2578,12 @@ fn render_call_locations(
sources::print_src(
w,
contents_subset,
edition,
call_data.edition,
file_span,
cx,
&root_path,
Some(min_line),
Some(decoration_info),
Some(line_offset),
Some(highlight::DecorationInfo(decoration_info)),
);
write!(w, "</div></div>");
@ -2590,12 +2602,14 @@ fn render_call_locations(
};
let mut locs = call_locations.into_iter().collect::<Vec<_>>();
locs.sort_by_key(|x| sort_criterion(x));
locs.sort_by_key(sort_criterion);
locs
};
// Write just one example that's visible by default in the method's description.
let mut it = ordered_locations.into_iter().peekable();
// An example may fail to write if its source can't be read for some reason, so this method
// continues iterating until a write suceeds
let write_and_skip_failure = |w: &mut Buffer, it: &mut Peekable<_>| {
while let Some(example) = it.next() {
if write_example(&mut *w, example) {
@ -2604,6 +2618,7 @@ fn render_call_locations(
}
};
// Write just one example that's visible by default in the method's description.
write_and_skip_failure(w, &mut it);
// Then add the remaining examples in a hidden section.
@ -2626,7 +2641,7 @@ fn render_call_locations(
write_and_skip_failure(w, &mut it);
}
// For the remaining examples, generate a <ul /> containing links to the source files.
// For the remaining examples, generate a <ul> containing links to the source files.
if it.peek().is_some() {
write!(
w,
@ -2635,7 +2650,7 @@ fn render_call_locations(
it.for_each(|(_, call_data)| {
write!(
w,
r#"<li><a href="{}{}" target="_blank">{}</a></li>"#,
r#"<li><a href="{root}{url}" target="_blank">{name}</a></li>"#,
root = cx.root_path(),
url = call_data.url,
name = call_data.display_name

View file

@ -1978,10 +1978,6 @@ details.undocumented[open] > summary::before {
font-family: 'Fira Sans';
}
.scraped-example-title a {
margin-left: 5px;
}
.scraped-example:not(.expanded) .code-wrapper pre.line-numbers,
.scraped-example:not(.expanded) .code-wrapper .example-wrap pre.rust {
overflow: hidden;

View file

@ -996,9 +996,11 @@ function hideThemeButtonState() {
function updateScrapedExample(example) {
var locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
var offset = parseInt(example.attributes.getNamedItem("data-offset").textContent);
var locIndex = 0;
var highlights = example.querySelectorAll('.highlight');
var link = example.querySelector('.scraped-example-title a');
addClass(highlights[0], 'focus');
if (locs.length > 1) {
// Toggle through list of examples in a given file
@ -1007,13 +1009,36 @@ function hideThemeButtonState() {
f();
scrollToLoc(example, locs[locIndex]);
addClass(highlights[locIndex], 'focus');
var curLoc = locs[locIndex];
var minLine = curLoc[0] + offset + 1;
var maxLine = curLoc[1] + offset + 1;
var text;
var anchor;
if (minLine == maxLine) {
text = 'line ' + minLine.toString();
anchor = minLine.toString();
} else {
var range = minLine.toString() + '-' + maxLine.toString();
text = 'lines ' + range;
anchor = range;
}
var url = new URL(link.href);
url.hash = anchor;
link.href = url.toString();
link.innerHTML = text;
};
example.querySelector('.prev')
.addEventListener('click', function() {
onChangeLoc(function() {
locIndex = (locIndex - 1 + locs.length) % locs.length;
});
});
example.querySelector('.next')
.addEventListener('click', function() {
onChangeLoc(function() { locIndex = (locIndex + 1) % locs.length; });

View file

@ -289,7 +289,7 @@ crate fn from_fn_header(header: &rustc_hir::FnHeader) -> HashSet<Qualifiers> {
impl FromWithTcx<clean::Function> for Function {
fn from_tcx(function: clean::Function, tcx: TyCtxt<'_>) -> Self {
let clean::Function { decl, generics, header, call_locations: _ } = function;
let clean::Function { decl, generics, header, def_id: _ } = function;
Function {
decl: decl.into_tcx(tcx),
generics: generics.into_tcx(tcx),
@ -530,7 +530,7 @@ crate fn from_function_method(
has_body: bool,
tcx: TyCtxt<'_>,
) -> Method {
let clean::Function { header, decl, generics, call_locations: _ } = function;
let clean::Function { header, decl, generics, def_id: _ } = function;
Method {
decl: decl.into_tcx(tcx),
generics: generics.into_tcx(tcx),

View file

@ -47,11 +47,13 @@ extern crate rustc_interface;
extern crate rustc_lexer;
extern crate rustc_lint;
extern crate rustc_lint_defs;
extern crate rustc_macros;
extern crate rustc_metadata;
extern crate rustc_middle;
extern crate rustc_parse;
extern crate rustc_passes;
extern crate rustc_resolve;
extern crate rustc_serialize;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_target;
@ -619,8 +621,22 @@ fn opts() -> Vec<RustcOptGroup> {
"Make the identifiers in the HTML source code pages navigable",
)
}),
unstable("scrape-examples", |o| o.optopt("", "scrape-examples", "", "")),
unstable("with-examples", |o| o.optmulti("", "with-examples", "", "")),
unstable("scrape-examples", |o| {
o.optopt(
"",
"scrape-examples",
"",
"collect function call information (for use with `--with-examples`)",
)
}),
unstable("with-examples", |o| {
o.optmulti(
"",
"with-examples",
"",
"path to function call information (for displaying examples in the documentation)",
)
}),
]
}

View file

@ -13,14 +13,19 @@ use rustc_hir::{
HirId,
};
use rustc_interface::interface;
use rustc_macros::{Decodable, Encodable};
use rustc_middle::hir::map::Map;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::{def_id::DefId, BytePos, FileName, SourceFile};
use serde::{Deserialize, Serialize};
use rustc_serialize::{
opaque::{Decoder, FileEncoder},
Decodable, Encodable,
};
use rustc_span::{def_id::DefId, edition::Edition, BytePos, FileName, SourceFile};
use std::fs;
use std::path::PathBuf;
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Encodable, Decodable, Debug, Clone)]
crate struct SyntaxRange {
crate byte_span: (u32, u32),
crate line_span: (usize, usize),
@ -38,7 +43,7 @@ impl SyntaxRange {
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Encodable, Decodable, Debug, Clone)]
crate struct CallLocation {
crate call_expr: SyntaxRange,
crate enclosing_item: SyntaxRange,
@ -49,9 +54,10 @@ impl CallLocation {
tcx: TyCtxt<'_>,
expr_span: rustc_span::Span,
expr_id: HirId,
source_file: &rustc_span::SourceFile,
source_file: &SourceFile,
) -> Self {
let enclosing_item_span = tcx.hir().span_with_body(tcx.hir().get_parent_item(expr_id));
let enclosing_item_span =
tcx.hir().span_with_body(tcx.hir().get_parent_item(expr_id)).source_callsite();
assert!(enclosing_item_span.contains(expr_span));
CallLocation {
@ -61,15 +67,16 @@ impl CallLocation {
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Encodable, Decodable, Debug, Clone)]
crate struct CallData {
crate locations: Vec<CallLocation>,
crate url: String,
crate display_name: String,
crate edition: Edition,
}
crate type DefIdCallKey = String;
crate type FnCallLocations = FxHashMap<PathBuf, CallData>;
crate type AllCallLocations = FxHashMap<DefIdCallKey, FnCallLocations>;
crate type AllCallLocations = FxHashMap<String, FnCallLocations>;
/// Visitor for traversing a crate and finding instances of function calls.
struct FindCalls<'a, 'tcx> {
@ -79,7 +86,7 @@ struct FindCalls<'a, 'tcx> {
calls: &'a mut AllCallLocations,
}
crate fn def_id_call_key(tcx: TyCtxt<'_>, def_id: DefId) -> DefIdCallKey {
crate fn def_id_call_key(tcx: TyCtxt<'_>, def_id: DefId) -> String {
format!(
"{}{}",
tcx.crate_name(def_id.krate).to_ident_string(),
@ -101,30 +108,29 @@ where
intravisit::walk_expr(self, ex);
// Get type of function if expression is a function call
let tcx = self.tcx;
let (ty, span) = match ex.kind {
hir::ExprKind::Call(f, _) => {
let types = self.tcx.typeck(ex.hir_id.owner);
let types = tcx.typeck(ex.hir_id.owner);
(types.node_type(f.hir_id), ex.span)
}
hir::ExprKind::MethodCall(_, _, _, span) => {
let types = self.tcx.typeck(ex.hir_id.owner);
let types = tcx.typeck(ex.hir_id.owner);
let def_id = types.type_dependent_def_id(ex.hir_id).unwrap();
(self.tcx.type_of(def_id), span)
(tcx.type_of(def_id), span)
}
_ => {
return;
}
};
if span.from_expansion() {
return;
}
// We need to get the file the example originates in. If the call is contained
// in a macro, then trace the span back to the macro source (rather than macro definition).
let span = span.source_callsite();
// Save call site if the function resolves to a concrete definition
if let ty::FnDef(def_id, _) = ty.kind() {
let fn_key = def_id_call_key(self.tcx, *def_id);
let entries = self.calls.entry(fn_key).or_insert_with(FxHashMap::default);
let file = self.tcx.sess.source_map().lookup_char_pos(span.lo()).file;
let file = tcx.sess.source_map().lookup_char_pos(span.lo()).file;
let file_path = match file.name.clone() {
FileName::Real(real_filename) => real_filename.into_local_path(),
_ => None,
@ -133,18 +139,19 @@ where
if let Some(file_path) = file_path {
let abs_path = fs::canonicalize(file_path.clone()).unwrap();
let cx = &self.cx;
let location = CallLocation::new(self.tcx, span, ex.hir_id, &file);
entries
.entry(abs_path)
.or_insert_with(|| {
let mk_call_data = || {
let clean_span = crate::clean::types::Span::new(span);
let url = cx.href_from_span(clean_span).unwrap();
let display_name = file_path.display().to_string();
CallData { locations: Vec::new(), url, display_name }
})
.locations
.push(location);
let edition = tcx.sess.edition();
CallData { locations: Vec::new(), url, display_name, edition }
};
let fn_key = def_id_call_key(tcx, *def_id);
let fn_entries = self.calls.entry(fn_key).or_default();
let location = CallLocation::new(tcx, span, ex.hir_id, &file);
fn_entries.entry(abs_path).or_insert_with(mk_call_data).locations.push(location);
}
}
}
@ -154,55 +161,54 @@ crate fn run(
krate: clean::Crate,
renderopts: config::RenderOptions,
cache: formats::cache::Cache,
tcx: TyCtxt<'tcx>,
tcx: TyCtxt<'_>,
example_path: PathBuf,
) -> interface::Result<()> {
let inner = move || {
let inner = move || -> Result<(), String> {
// Generates source files for examples
let (cx, _) = Context::init(krate, renderopts, cache, tcx).map_err(|e| format!("{}", e))?;
let (cx, _) = Context::init(krate, renderopts, cache, tcx).map_err(|e| e.to_string())?;
// Run call-finder on all items
let mut calls = FxHashMap::default();
let mut finder = FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx };
tcx.hir().krate().visit_all_item_likes(&mut finder.as_deep_visitor());
// Save output JSON to provided path
let calls_json = serde_json::to_string(&calls).map_err(|e| format!("{}", e))?;
fs::write(example_path, &calls_json).map_err(|e| format!("{}", e))?;
// Save output to provided path
let mut encoder = FileEncoder::new(example_path).map_err(|e| e.to_string())?;
calls.encode(&mut encoder).map_err(|e| e.to_string())?;
encoder.flush().map_err(|e| e.to_string())?;
Ok(())
};
inner().map_err(|e: String| {
eprintln!("{}", e);
rustc_errors::ErrorReported
})
if let Err(e) = inner() {
tcx.sess.fatal(&e);
}
Ok(())
}
crate fn load_call_locations(
with_examples: Vec<String>,
diag: &rustc_errors::Handler,
) -> Result<Option<AllCallLocations>, i32> {
let each_call_locations = with_examples
.into_iter()
.map(|path| {
) -> Result<AllCallLocations, i32> {
let inner = || {
let mut all_calls: AllCallLocations = FxHashMap::default();
for path in with_examples {
let bytes = fs::read(&path).map_err(|e| format!("{} (for path {})", e, path))?;
let calls: AllCallLocations =
serde_json::from_slice(&bytes).map_err(|e| format!("{}", e))?;
Ok(calls)
})
.collect::<Result<Vec<_>, _>>()
.map_err(|e: String| {
diag.err(&format!("failed to load examples with error: {}", e));
1
})?;
let mut decoder = Decoder::new(&bytes, 0);
let calls = AllCallLocations::decode(&mut decoder)?;
Ok((each_call_locations.len() > 0).then(|| {
each_call_locations.into_iter().fold(FxHashMap::default(), |mut acc, map| {
for (function, calls) in map.into_iter() {
acc.entry(function).or_insert_with(FxHashMap::default).extend(calls.into_iter());
for (function, fn_calls) in calls.into_iter() {
all_calls.entry(function).or_default().extend(fn_calls.into_iter());
}
acc
}
Ok(all_calls)
};
inner().map_err(|e: String| {
diag.err(&format!("failed to load examples: {}", e));
1
})
}))
}