flowistry_ide/focus/
mod.rs1use anyhow::Result;
2use flowistry::infoflow::{self, Direction};
3use itertools::Itertools;
4use rustc_hir::BodyId;
5use rustc_middle::ty::TyCtxt;
6use rustc_span::Span;
7use rustc_utils::{
8 SpanExt,
9 mir::borrowck_facts::get_body_with_borrowck_facts,
10 source_map::{
11 range::CharRange,
12 spanner::{EnclosingHirSpans, Spanner},
13 },
14};
15use serde::Serialize;
16
17mod direct_influence;
18
19#[derive(Debug, Serialize)]
20pub struct PlaceInfo {
21 pub range: CharRange,
22 pub ranges: Vec<CharRange>,
23 pub slice: Vec<CharRange>,
24 pub direct_influence: Vec<CharRange>,
25}
26
27#[derive(Debug, Serialize)]
28pub struct FocusOutput {
29 pub place_info: Vec<PlaceInfo>,
30 pub containers: Vec<CharRange>,
31}
32
33pub fn focus(tcx: TyCtxt, body_id: BodyId) -> Result<FocusOutput> {
34 let def_id = tcx.hir_body_owner_def_id(body_id);
35 let body_with_facts = get_body_with_borrowck_facts(tcx, def_id);
36 let body = &body_with_facts.body;
37 let results = &infoflow::compute_flow(tcx, body_id, body_with_facts);
38
39 let source_map = tcx.sess.source_map();
40 let spanner = Spanner::new(tcx, body_id, body);
41
42 let grouped_spans = spanner
43 .mir_span_tree
44 .iter()
45 .map(|mir_span| {
46 (
47 mir_span.span,
48 mir_span
49 .locations
50 .iter()
51 .map(|location| (mir_span.place, *location))
52 .collect::<Vec<_>>(),
53 )
54 })
55 .into_group_map()
56 .into_iter()
57 .map(|(k, vs)| (k, vs.concat()))
58 .collect::<Vec<_>>();
59
60 let targets = grouped_spans
61 .iter()
62 .map(|(_, target)| target.clone())
63 .collect();
64
65 let relevant =
66 infoflow::compute_dependency_spans(results, targets, Direction::Both, &spanner);
67
68 let direct =
69 direct_influence::DirectInfluence::build(body, &results.analysis.place_info);
70
71 let slices = grouped_spans
72 .iter()
73 .zip(relevant)
74 .filter_map(|((mir_span, targets), relevant)| {
75 log::debug!("Slice for {mir_span:?} is {relevant:#?}");
76
77 let direct_influence = targets
78 .iter()
79 .flat_map(|(target, _)| direct.lookup(*target))
80 .flat_map(|location| {
81 spanner.location_to_spans(location, body, EnclosingHirSpans::None)
82 })
83 .filter(|span| relevant.iter().any(|slice_span| slice_span.contains(*span)))
84 .collect::<Vec<_>>();
85
86 let slice = relevant;
87
88 let to_ranges = |v: Vec<Span>| {
89 v.into_iter()
90 .filter_map(|span| span.trim_leading_whitespace(source_map))
91 .flatten()
92 .filter_map(|span| CharRange::from_span(span, source_map).ok())
93 .collect::<Vec<_>>()
94 };
95
96 log::debug!("{:#?}", to_ranges(slice.clone()));
97
98 Some(PlaceInfo {
99 range: CharRange::from_span(mir_span.span(), source_map).ok()?,
100 ranges: to_ranges(vec![mir_span.span()]),
101 slice: to_ranges(slice),
102 direct_influence: to_ranges(direct_influence),
103 })
104 })
105 .collect::<Vec<_>>();
106
107 let body_range = CharRange::from_span(spanner.body_span, source_map)?;
108 let ret_range = CharRange::from_span(spanner.ret_span, source_map)?;
109 let mut containers = vec![body_range, ret_range];
110
111 let hir_body = tcx.hir_body(body_id);
112 let arg_span = hir_body
113 .params
114 .iter()
115 .map(|param| param.span)
116 .reduce(|s1, s2| s1.to(s2));
117 if let Some(sp) = arg_span {
118 containers.push(CharRange::from_span(sp, source_map)?);
119 }
120
121 Ok(FocusOutput {
122 place_info: slices,
123 containers,
124 })
125}