1
Fork 0

revamp MultiSpan and introduce new snippet code

MultiSpan model is now:

- set of primary spans
- set of span+label pairs

Primary spans render with `^^^`, secondary spans with `---`.

Labels are placed next to the `^^^` or `---` marker as appropriate.
This commit is contained in:
Niko Matsakis 2016-04-20 14:52:31 -04:00
parent e1a575cb07
commit a20ee76b56
3 changed files with 1433 additions and 140 deletions

View file

@ -32,8 +32,6 @@ use serialize::{Encodable, Decodable, Encoder, Decoder};
use ast::Name;
use errors::emitter::MAX_HIGHLIGHT_LINES;
// _____________________________________________________________________________
// Pos, BytePos, CharPos
//
@ -51,7 +49,7 @@ pub struct BytePos(pub u32);
/// A character offset. Because of multibyte utf8 characters, a byte offset
/// is not equivalent to a character offset. The CodeMap will convert BytePos
/// values to CharPos values as necessary.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct CharPos(pub usize);
// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
@ -132,13 +130,29 @@ pub struct Span {
pub expn_id: ExpnId
}
/// Spans are converted to MultiSpans just before error reporting, either automatically,
/// generated by line grouping, or manually constructed.
/// In the latter case care should be taken to ensure that spans are ordered, disjoint,
/// and point into the same FileMap.
/// A collection of spans. Spans have two orthogonal attributes:
///
/// - they can be *primary spans*. In this case they are the locus of
/// the error, and would be rendered with `^^^`.
/// - they can have a *label*. In this case, the label is written next
/// to the mark in the snippet when we render.
#[derive(Clone)]
pub struct MultiSpan {
pub spans: Vec<Span>
primary_spans: Vec<Span>,
span_labels: Vec<(Span, String)>,
}
#[derive(Clone, Debug)]
pub struct SpanLabel {
/// the span we are going to include in the final snippet
pub span: Span,
/// is this a primary span? This is the "locus" of the message,
/// and is indicated with a `^^^^` underline, versus `----`
pub is_primary: bool,
/// what label should we attach to this span (if any)?
pub label: Option<String>,
}
pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), expn_id: NO_EXPANSION };
@ -276,97 +290,76 @@ pub fn original_sp(cm: &CodeMap, sp: Span, enclosing_sp: Span) -> Span {
impl MultiSpan {
pub fn new() -> MultiSpan {
MultiSpan { spans: Vec::new() }
}
pub fn to_span_bounds(&self) -> Span {
assert!(!self.spans.is_empty());
let Span { lo, expn_id, .. } = *self.spans.first().unwrap();
let Span { hi, .. } = *self.spans.last().unwrap();
Span { lo: lo, hi: hi, expn_id: expn_id }
}
/// Merges or inserts the given span into itself.
pub fn push_merge(&mut self, mut sp: Span) {
let mut idx_merged = None;
for idx in 0.. {
let cur = match self.spans.get(idx) {
Some(s) => *s,
None => break,
};
// Try to merge with a contained Span
if let Some(union) = cur.merge(sp) {
self.spans[idx] = union;
sp = union;
idx_merged = Some(idx);
break;
}
// Or insert into the first sorted position
if sp.hi <= cur.lo {
self.spans.insert(idx, sp);
idx_merged = Some(idx);
break;
}
}
if let Some(idx) = idx_merged {
// Merge with spans trailing the insertion/merging position
while (idx + 1) < self.spans.len() {
if let Some(union) = self.spans[idx + 1].merge(sp) {
self.spans[idx] = union;
self.spans.remove(idx + 1);
} else {
break;
}
}
} else {
self.spans.push(sp);
MultiSpan {
primary_spans: vec![],
span_labels: vec![]
}
}
/// Inserts the given span into itself, for use with `end_highlight_lines`.
pub fn push_trim(&mut self, mut sp: Span) {
let mut prev = mk_sp(BytePos(0), BytePos(0));
pub fn from_span(primary_span: Span) -> MultiSpan {
MultiSpan {
primary_spans: vec![primary_span],
span_labels: vec![]
}
}
if let Some(first) = self.spans.get_mut(0) {
if first.lo > sp.lo {
// Prevent us here from spanning fewer lines
// because of trimming the start of the span
// (this should not be visible, because this method ought
// to not be used in conjunction with `highlight_lines`)
first.lo = sp.lo;
pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
MultiSpan {
primary_spans: vec,
span_labels: vec![]
}
}
pub fn push_primary_span(&mut self, span: Span) {
self.primary_spans.push(span);
}
pub fn push_span_label(&mut self, span: Span, label: String) {
self.span_labels.push((span, label));
}
/// Selects the first primary span (if any)
pub fn primary_span(&self) -> Option<Span> {
self.primary_spans.first().cloned()
}
/// Returns all primary spans.
pub fn primary_spans(&self) -> &[Span] {
&self.primary_spans
}
/// Returns the strings to highlight. If we have an explicit set,
/// return those, otherwise just give back an (unlabeled) version
/// of the primary span.
pub fn span_labels(&self) -> Vec<SpanLabel> {
let is_primary = |span| self.primary_spans.contains(&span);
let mut span_labels = vec![];
for &(span, ref label) in &self.span_labels {
span_labels.push(SpanLabel {
span: span,
is_primary: is_primary(span),
label: Some(label.clone())
});
}
for &span in &self.primary_spans {
if !span_labels.iter().any(|sl| sl.span == span) {
span_labels.push(SpanLabel {
span: span,
is_primary: true,
label: None
});
}
}
for idx in 0.. {
if let Some(sp_trim) = sp.trim_start(prev) {
// Implies `sp.hi > prev.hi`
let cur = match self.spans.get(idx) {
Some(s) => *s,
None => {
sp = sp_trim;
break;
}
};
// `cur` may overlap with `sp_trim`
if let Some(cur_trim) = cur.trim_start(sp_trim) {
// Implies `sp.hi < cur.hi`
self.spans.insert(idx, sp_trim);
self.spans[idx + 1] = cur_trim;
return;
} else if sp.hi == cur.hi {
return;
}
prev = cur;
}
}
self.spans.push(sp);
span_labels
}
}
impl From<Span> for MultiSpan {
fn from(span: Span) -> MultiSpan {
MultiSpan { spans: vec![span] }
MultiSpan::from_span(span)
}
}
@ -929,6 +922,10 @@ impl CodeMap {
}
pub fn span_to_string(&self, sp: Span) -> String {
if sp == COMMAND_LINE_SP {
return "<command line option>".to_string();
}
if self.files.borrow().is_empty() && sp.source_equal(&DUMMY_SP) {
return "no-location".to_string();
}
@ -1099,12 +1096,16 @@ impl CodeMap {
}
pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
debug!("span_to_lines(sp={:?})", sp);
if sp.lo > sp.hi {
return Err(SpanLinesError::IllFormedSpan(sp));
}
let lo = self.lookup_char_pos(sp.lo);
debug!("span_to_lines: lo={:?}", lo);
let hi = self.lookup_char_pos(sp.hi);
debug!("span_to_lines: hi={:?}", hi);
if lo.file.start_pos != hi.file.start_pos {
return Err(SpanLinesError::DistinctSources(DistinctSources {
@ -1184,59 +1185,6 @@ impl CodeMap {
}
}
/// Groups and sorts spans by lines into `MultiSpan`s, where `push` adds them to their group,
/// specifying the unification behaviour for overlapping spans.
/// Spans overflowing a line are put into their own one-element-group.
pub fn custom_group_spans<F>(&self, mut spans: Vec<Span>, push: F) -> Vec<MultiSpan>
where F: Fn(&mut MultiSpan, Span)
{
spans.sort_by(|a, b| a.lo.cmp(&b.lo));
let mut groups = Vec::<MultiSpan>::new();
let mut overflowing = vec![];
let mut prev_expn = ExpnId(!2u32);
let mut prev_file = !0usize;
let mut prev_line = !0usize;
let mut err_size = 0;
for sp in spans {
let line = self.lookup_char_pos(sp.lo).line;
let line_hi = self.lookup_char_pos(sp.hi).line;
if line != line_hi {
overflowing.push(sp.into());
continue
}
let file = self.lookup_filemap_idx(sp.lo);
if err_size < MAX_HIGHLIGHT_LINES && sp.expn_id == prev_expn && file == prev_file {
// `push` takes care of sorting, trimming, and merging
push(&mut groups.last_mut().unwrap(), sp);
if line != prev_line {
err_size += 1;
}
} else {
groups.push(sp.into());
err_size = 1;
}
prev_expn = sp.expn_id;
prev_file = file;
prev_line = line;
}
groups.extend(overflowing);
groups
}
/// Groups and sorts spans by lines into `MultiSpan`s, merging overlapping spans.
/// Spans overflowing a line are put into their own one-element-group.
pub fn group_spans(&self, spans: Vec<Span>) -> Vec<MultiSpan> {
self.custom_group_spans(spans, |msp, sp| msp.push_merge(sp))
}
/// Like `group_spans`, but trims overlapping spans instead of
/// merging them (for use with `end_highlight_lines`)
pub fn end_group_spans(&self, spans: Vec<Span>) -> Vec<MultiSpan> {
self.custom_group_spans(spans, |msp, sp| msp.push_trim(sp))
}
pub fn get_filemap(&self, filename: &str) -> Rc<FileMap> {
for fm in self.files.borrow().iter() {
if filename == fm.name {