flowistry_ide/focus/
mod.rs

1use 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}