Auto merge of #36585 - jonathandturner:misc_error_touchups, r=nrc
Add the ability to merge spans to codemap This PR adds the ability to merge Spans. To do so, it builds on the Codemap's ability to verify the locations of spans, namely that following can be verified: * the expn_id of both spans much match * the lhs span needs to end on the same line the rhs span begins * the lhs span must start at or before the rhs span If all of these are met, a new span is returned that is min(lo), max(hi) of the two spans. This PR also removes an older Span merge, as this new functionality subsumes it. r? @nrc
This commit is contained in:
commit
6ad10844db
@ -81,6 +81,7 @@ pub trait CodeMapper {
|
||||
fn span_to_string(&self, sp: Span) -> String;
|
||||
fn span_to_filename(&self, sp: Span) -> FileName;
|
||||
fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace>;
|
||||
fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>;
|
||||
}
|
||||
|
||||
impl CodeSuggestion {
|
||||
|
@ -364,6 +364,46 @@ impl CodeMap {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Some(span)`, a union of the lhs and rhs span. The lhs must precede the rhs. If
|
||||
/// there are gaps between lhs and rhs, the resulting union will cross these gaps.
|
||||
/// For this to work, the spans have to be:
|
||||
/// * the expn_id of both spans much match
|
||||
/// * the lhs span needs to end on the same line the rhs span begins
|
||||
/// * the lhs span must start at or before the rhs span
|
||||
pub fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
|
||||
use std::cmp;
|
||||
|
||||
// make sure we're at the same expansion id
|
||||
if sp_lhs.expn_id != sp_rhs.expn_id {
|
||||
return None;
|
||||
}
|
||||
|
||||
let lhs_end = match self.lookup_line(sp_lhs.hi) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return None
|
||||
};
|
||||
let rhs_begin = match self.lookup_line(sp_rhs.lo) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return None
|
||||
};
|
||||
|
||||
// if we must cross lines to merge, don't merge
|
||||
if lhs_end.line != rhs_begin.line {
|
||||
return None;
|
||||
}
|
||||
|
||||
// ensure these follow the expected order and we don't overlap
|
||||
if (sp_lhs.lo <= sp_rhs.lo) && (sp_lhs.hi <= sp_rhs.lo) {
|
||||
Some(Span {
|
||||
lo: cmp::min(sp_lhs.lo, sp_rhs.lo),
|
||||
hi: cmp::max(sp_lhs.hi, sp_rhs.hi),
|
||||
expn_id: sp_lhs.expn_id,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span_to_string(&self, sp: Span) -> String {
|
||||
if sp == COMMAND_LINE_SP {
|
||||
return "<command line option>".to_string();
|
||||
@ -819,6 +859,9 @@ impl CodeMapper for CodeMap {
|
||||
fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace> {
|
||||
self.macro_backtrace(span)
|
||||
}
|
||||
fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
|
||||
self.merge_spans(sp_lhs, sp_rhs)
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
@ -1072,6 +1115,40 @@ mod tests {
|
||||
blork.rs:1:1: 1:12\n `first line.`\n");
|
||||
}
|
||||
|
||||
/// Test merging two spans on the same line
|
||||
#[test]
|
||||
fn span_merging() {
|
||||
let cm = CodeMap::new();
|
||||
let inputtext = "bbbb BB bb CCC\n";
|
||||
let selection1 = " ~~ \n";
|
||||
let selection2 = " ~~~\n";
|
||||
cm.new_filemap_and_lines("blork.rs", None, inputtext);
|
||||
let span1 = span_from_selection(inputtext, selection1);
|
||||
let span2 = span_from_selection(inputtext, selection2);
|
||||
|
||||
if let Some(sp) = cm.merge_spans(span1, span2) {
|
||||
let sstr = cm.span_to_expanded_string(sp);
|
||||
assert_eq!(sstr, "blork.rs:1:6: 1:15\n`BB bb CCC`\n");
|
||||
}
|
||||
else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Test failing to merge two spans on different lines
|
||||
#[test]
|
||||
fn span_merging_fail() {
|
||||
let cm = CodeMap::new();
|
||||
let inputtext = "bbbb BB\ncc CCC\n";
|
||||
let selection1 = " ~~\n \n";
|
||||
let selection2 = " \n ~~~\n";
|
||||
cm.new_filemap_and_lines("blork.rs", None, inputtext);
|
||||
let span1 = span_from_selection(inputtext, selection1);
|
||||
let span2 = span_from_selection(inputtext, selection2);
|
||||
|
||||
assert!(cm.merge_spans(span1, span2).is_none());
|
||||
}
|
||||
|
||||
/// Returns the span corresponding to the `n`th occurrence of
|
||||
/// `substring` in `source_text`.
|
||||
trait CodeMapExtension {
|
||||
|
@ -97,24 +97,6 @@ impl Span {
|
||||
self.lo == other.lo && self.hi == other.hi
|
||||
}
|
||||
|
||||
/// Returns `Some(span)`, a union of `self` and `other`, on overlap.
|
||||
pub fn merge(self, other: Span) -> Option<Span> {
|
||||
if self.expn_id != other.expn_id {
|
||||
return None;
|
||||
}
|
||||
|
||||
if (self.lo <= other.lo && self.hi > other.lo) ||
|
||||
(self.lo >= other.lo && self.lo < other.hi) {
|
||||
Some(Span {
|
||||
lo: cmp::min(self.lo, other.lo),
|
||||
hi: cmp::max(self.hi, other.hi),
|
||||
expn_id: self.expn_id,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Some(span)`, where the start is trimmed by the end of `other`
|
||||
pub fn trim_start(self, other: Span) -> Option<Span> {
|
||||
if self.hi > other.hi {
|
||||
|
Loading…
Reference in New Issue
Block a user