Skip to content
This repository was archived by the owner on Mar 1, 2019. It is now read-only.

Commit 3bf3f2e

Browse files
committed
Add capability to quickly find id for closest matching fn-like span
1 parent 33941ba commit 3bf3f2e

File tree

4 files changed

+152
-20
lines changed

4 files changed

+152
-20
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lib.rs

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,17 @@ mod test;
2626

2727
pub use self::raw::{Target, name_space_for_def_kind, read_analyis_incremental};
2828

29+
mod search_span;
30+
use search_span::*;
31+
pub use search_span::Span;
32+
2933
use std::collections::HashMap;
3034
use std::ffi::OsStr;
3135
use std::path::{Path, PathBuf};
3236
use std::process::Command;
3337
use std::sync::Mutex;
3438
use std::time::{Instant, SystemTime};
39+
use std::collections::BTreeMap;
3540

3641
pub struct AnalysisHost<L: AnalysisLoader = CargoAnalysisLoader> {
3742
analysis: Mutex<Option<Analysis>>,
@@ -430,24 +435,41 @@ impl<L: AnalysisLoader> AnalysisHost<L> {
430435
pub fn borrow_info(&self, span: &Span) -> AResult<BorrowData> {
431436
self.with_analysis(|a|
432437
a.def_id_for_span(span)
433-
.and_then(|id| a.for_each_crate(|c| {
434-
trace!("Searching in crate `{}`", c.name);
435-
for (_, b) in c.per_fn_borrows.iter() {
436-
// If we find a `BorrowData` where there's a matching scope, then filter out
437-
// out matching items.
438-
if b.scopes.iter().any(|a| a.ref_id == id) {
438+
.and_then(|id| {
439+
let fn_id = match a.fn_id_for_span(span) {
440+
Some(fn_id) => fn_id,
441+
None => { return None; }
442+
};
443+
a.for_each_crate(|c| {
444+
let borrow_data = match c.per_fn_borrows.get(&fn_id) {
445+
Some(borrow_data) => borrow_data,
446+
None => { return None; }
447+
};
448+
449+
if borrow_data.scopes.iter().any(|a| a.ref_id == id) {
450+
// If we find a `BorrowData` where there's a matching scope, then filter out
451+
// out matching items.
439452
trace!("Found borrow for id `{}` in crate `{}`", id, c.name);
440-
return Some(BorrowData {
441-
ref_id: b.ref_id,
442-
scopes: b.scopes.iter().filter(|s| s.ref_id == id).map(|s| s.clone()).collect(),
443-
loans: b.loans.iter().filter(|l| l.ref_id == id).map(|l| l.clone()).collect(),
444-
moves: b.moves.iter().filter(|m| m.ref_id == id).map(|m| m.clone()).collect(),
445-
});
453+
Some(BorrowData {
454+
ref_id: borrow_data.ref_id,
455+
scopes: borrow_data.scopes.iter()
456+
.filter(|s| s.ref_id == id)
457+
.map(|s| s.clone())
458+
.collect(),
459+
loans: borrow_data.loans.iter()
460+
.filter(|l| l.ref_id == id)
461+
.map(|l| l.clone())
462+
.collect(),
463+
moves: borrow_data.moves.iter()
464+
.filter(|m| m.ref_id == id)
465+
.map(|m| m.clone())
466+
.collect(),
467+
})
468+
} else {
469+
None
446470
}
447-
}
448-
449-
None
450-
}))
471+
})
472+
})
451473
)
452474
}
453475

@@ -569,8 +591,6 @@ impl SymbolResult {
569591
}
570592
}
571593

572-
type Span = span::Span<span::ZeroIndexed>;
573-
574594
#[derive(Debug)]
575595
pub struct Analysis {
576596
// The primary crate will have its data passed directly, not via a file, so
@@ -594,6 +614,7 @@ pub struct PerCrateAnalysis {
594614
globs: HashMap<Span, Glob>,
595615
impls: HashMap<Id, Vec<Span>>,
596616
per_fn_borrows: HashMap<Id, BorrowData>,
617+
fn_span_lookup_tree: BTreeMap<SearchSpan, Id>,
597618

598619
name: String,
599620
root_id: Option<Id>,
@@ -680,6 +701,7 @@ impl PerCrateAnalysis {
680701
globs: HashMap::new(),
681702
impls: HashMap::new(),
682703
per_fn_borrows: HashMap::new(),
704+
fn_span_lookup_tree: BTreeMap::new(),
683705
name: String::new(),
684706
root_id: None,
685707
timestamp: None,
@@ -738,6 +760,18 @@ impl Analysis {
738760
self.for_each_crate(|c| c.def_id_for_span.get(span).cloned())
739761
}
740762

763+
fn fn_id_for_span(&self, span: &Span) -> Option<Id> {
764+
self.for_each_crate(|c|
765+
c.fn_span_lookup_tree
766+
// SearchSpan is designed to return all keys where the search
767+
// span is within the key span.
768+
.range(SearchSpan::range(span))
769+
// We want the key that's farthest in the file because that will be the smallest
770+
// scope that contains the span we're searching by
771+
.max_by(|&(left, _), &(right, _)| left.span().range.cmp(&right.span().range))
772+
.map(|(_, &id)| id))
773+
}
774+
741775
fn with_defs<F, T>(&self, id: Id, f: F) -> Option<T>
742776
where F: Fn(&Def) -> T
743777
{

src/lowering.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010

1111
use data;
1212
use raw::{self, Format, RelationKind, };
13-
use super::{AnalysisHost, AnalysisLoader, PerCrateAnalysis, AResult, Span, NULL, Def, Glob,
13+
use super::{AnalysisHost, AnalysisLoader, PerCrateAnalysis, AResult, NULL, Def, Glob,
1414
Id, BorrowData, Scope, Loan, Move, BorrowKind};
15+
use super::search_span::{Span, SearchSpan};
1516
use util;
1617

1718
use span;
@@ -271,6 +272,9 @@ impl CrateReader {
271272
}
272273
};
273274

275+
let fn_span = lower_span(&b.span.expect("All borrow data structs should have spans"), &self.base_dir);
276+
analysis.fn_span_lookup_tree.insert(SearchSpan::in_tree(&fn_span), def_id);
277+
274278
let scopes = b.scopes.into_iter().filter_map(|a|
275279
abs_ref_id(self.id_from_compiler_id(&a.ref_id), analysis, project_analysis)
276280
.map(|id| Scope {

src/search_span.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use std::cmp::Ordering;
2+
use std::ops::Range;
3+
use span;
4+
5+
pub type Span = span::Span<span::ZeroIndexed>;
6+
7+
#[derive(Debug)]
8+
enum SpanKind {
9+
InTree, // Span keys tracked inside the BTreeMap
10+
Start, // Start range to search for
11+
End // End range to search for
12+
}
13+
14+
#[derive(Debug)]
15+
pub struct SearchSpan(Span, SpanKind);
16+
17+
impl SearchSpan {
18+
pub fn range(span: &Span) -> Range<SearchSpan> {
19+
SearchSpan(span.clone(), SpanKind::Start)..SearchSpan(span.clone(), SpanKind::End)
20+
}
21+
22+
pub fn in_tree(span: &Span) -> Self {
23+
SearchSpan(span.clone(), SpanKind::InTree)
24+
}
25+
26+
pub fn span(&self) -> &Span {
27+
&self.0
28+
}
29+
}
30+
31+
impl PartialEq<SearchSpan> for SearchSpan {
32+
fn eq(&self, other: &SearchSpan) -> bool {
33+
match (&self.1, &other.1) {
34+
// In order to be stored inside the BTreeMap, InTree spans should always
35+
// equate and compare normally. When we compare start and end search spans,
36+
// they should also equate normally to satisfy some properties of the BTreeMap.
37+
// All other cases should not equate so we can properly collect the range.
38+
// Additional details are in the Ord::cmp implementation.
39+
(&SpanKind::InTree, &SpanKind::InTree)
40+
| (&SpanKind::Start, &SpanKind::End)
41+
| (&SpanKind::End, &SpanKind::Start) => self.0 == other.0,
42+
_ => false,
43+
}
44+
45+
}
46+
}
47+
impl Eq for SearchSpan { }
48+
49+
impl Ord for SearchSpan {
50+
fn cmp(&self, other: &SearchSpan) -> Ordering {
51+
if *self == *other {
52+
return Ordering::Equal
53+
}
54+
55+
// Answers the question, "Does Span b wrap Span a?"
56+
fn is_a_in_b(a: &Span, b: &Span) -> bool {
57+
!(b.range.row_start > a.range.row_start
58+
|| (b.range.row_start == a.range.row_start && b.range.col_start > a.range.col_start)
59+
|| b.range.row_end < a.range.row_end
60+
|| (b.range.row_end == a.range.row_end && b.range.col_end < a.range.col_end))
61+
}
62+
63+
match (self, other) {
64+
// If we're comparing two in-tree spans, they should be compared normally.
65+
(&SearchSpan(ref s1, SpanKind::InTree), &SearchSpan(ref s2, SpanKind::InTree)) => s1.cmp(s2),
66+
67+
// If the in-tree span wraps our start search span, we want to mark it as greater
68+
// so that it will be "inside" the range relating to the start search span.
69+
// If the in-tree span wraps our end search span, we want the end search to be
70+
// marked as greater to show that the in-tree span is "inside" the range.
71+
(&SearchSpan(ref in_tree, SpanKind::InTree), &SearchSpan(ref search, SpanKind::Start))
72+
| (&SearchSpan(ref search, SpanKind::End), &SearchSpan(ref in_tree, SpanKind::InTree))
73+
if is_a_in_b(search, in_tree) => Ordering::Greater,
74+
75+
// If the in-tree span wraps our end search span, we want to mark it as less
76+
// so that it will be "inside" the range relative to the end search span.
77+
// If the in-tree span wraps our start search span, we want the start search to be
78+
// marked as less to show that the in-tree span is "inside" the range.
79+
(&SearchSpan(ref in_tree, SpanKind::InTree), &SearchSpan(ref search, SpanKind::End))
80+
| (&SearchSpan(ref search, SpanKind::Start), &SearchSpan(ref in_tree, SpanKind::InTree))
81+
if is_a_in_b(in_tree, search) => Ordering::Less,
82+
83+
// These other cases are where the start and end search spans are not inside
84+
// the in-tree spans, and so we compare the spans normally in order to guide the
85+
// BTreeMap search down the proper arms of the tree.
86+
(&SearchSpan(ref left, _), &SearchSpan(ref right, _)) => left.cmp(right),
87+
}
88+
}
89+
}
90+
impl PartialOrd<SearchSpan> for SearchSpan {
91+
fn partial_cmp(&self, other: &SearchSpan) -> Option<Ordering> {
92+
Some(self.cmp(other))
93+
}
94+
}

0 commit comments

Comments
 (0)