Skip to content

Commit a20ee76

Browse files
committed
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.
1 parent e1a575c commit a20ee76

File tree

3 files changed

+1428
-135
lines changed

3 files changed

+1428
-135
lines changed

src/libsyntax/codemap.rs

Lines changed: 83 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ use serialize::{Encodable, Decodable, Encoder, Decoder};
3232

3333
use ast::Name;
3434

35-
use errors::emitter::MAX_HIGHLIGHT_LINES;
36-
3735
// _____________________________________________________________________________
3836
// Pos, BytePos, CharPos
3937
//
@@ -51,7 +49,7 @@ pub struct BytePos(pub u32);
5149
/// A character offset. Because of multibyte utf8 characters, a byte offset
5250
/// is not equivalent to a character offset. The CodeMap will convert BytePos
5351
/// values to CharPos values as necessary.
54-
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Debug)]
52+
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
5553
pub struct CharPos(pub usize);
5654

5755
// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
@@ -132,13 +130,29 @@ pub struct Span {
132130
pub expn_id: ExpnId
133131
}
134132

135-
/// Spans are converted to MultiSpans just before error reporting, either automatically,
136-
/// generated by line grouping, or manually constructed.
137-
/// In the latter case care should be taken to ensure that spans are ordered, disjoint,
138-
/// and point into the same FileMap.
133+
/// A collection of spans. Spans have two orthogonal attributes:
134+
///
135+
/// - they can be *primary spans*. In this case they are the locus of
136+
/// the error, and would be rendered with `^^^`.
137+
/// - they can have a *label*. In this case, the label is written next
138+
/// to the mark in the snippet when we render.
139139
#[derive(Clone)]
140140
pub struct MultiSpan {
141-
pub spans: Vec<Span>
141+
primary_spans: Vec<Span>,
142+
span_labels: Vec<(Span, String)>,
143+
}
144+
145+
#[derive(Clone, Debug)]
146+
pub struct SpanLabel {
147+
/// the span we are going to include in the final snippet
148+
pub span: Span,
149+
150+
/// is this a primary span? This is the "locus" of the message,
151+
/// and is indicated with a `^^^^` underline, versus `----`
152+
pub is_primary: bool,
153+
154+
/// what label should we attach to this span (if any)?
155+
pub label: Option<String>,
142156
}
143157

144158
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 {
276290

277291
impl MultiSpan {
278292
pub fn new() -> MultiSpan {
279-
MultiSpan { spans: Vec::new() }
293+
MultiSpan {
294+
primary_spans: vec![],
295+
span_labels: vec![]
296+
}
280297
}
281298

282-
pub fn to_span_bounds(&self) -> Span {
283-
assert!(!self.spans.is_empty());
284-
let Span { lo, expn_id, .. } = *self.spans.first().unwrap();
285-
let Span { hi, .. } = *self.spans.last().unwrap();
286-
Span { lo: lo, hi: hi, expn_id: expn_id }
299+
pub fn from_span(primary_span: Span) -> MultiSpan {
300+
MultiSpan {
301+
primary_spans: vec![primary_span],
302+
span_labels: vec![]
303+
}
287304
}
288305

289-
/// Merges or inserts the given span into itself.
290-
pub fn push_merge(&mut self, mut sp: Span) {
291-
let mut idx_merged = None;
292-
293-
for idx in 0.. {
294-
let cur = match self.spans.get(idx) {
295-
Some(s) => *s,
296-
None => break,
297-
};
298-
// Try to merge with a contained Span
299-
if let Some(union) = cur.merge(sp) {
300-
self.spans[idx] = union;
301-
sp = union;
302-
idx_merged = Some(idx);
303-
break;
304-
}
305-
// Or insert into the first sorted position
306-
if sp.hi <= cur.lo {
307-
self.spans.insert(idx, sp);
308-
idx_merged = Some(idx);
309-
break;
310-
}
311-
}
312-
if let Some(idx) = idx_merged {
313-
// Merge with spans trailing the insertion/merging position
314-
while (idx + 1) < self.spans.len() {
315-
if let Some(union) = self.spans[idx + 1].merge(sp) {
316-
self.spans[idx] = union;
317-
self.spans.remove(idx + 1);
318-
} else {
319-
break;
320-
}
321-
}
322-
} else {
323-
self.spans.push(sp);
306+
pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
307+
MultiSpan {
308+
primary_spans: vec,
309+
span_labels: vec![]
324310
}
325311
}
326312

327-
/// Inserts the given span into itself, for use with `end_highlight_lines`.
328-
pub fn push_trim(&mut self, mut sp: Span) {
329-
let mut prev = mk_sp(BytePos(0), BytePos(0));
313+
pub fn push_primary_span(&mut self, span: Span) {
314+
self.primary_spans.push(span);
315+
}
330316

331-
if let Some(first) = self.spans.get_mut(0) {
332-
if first.lo > sp.lo {
333-
// Prevent us here from spanning fewer lines
334-
// because of trimming the start of the span
335-
// (this should not be visible, because this method ought
336-
// to not be used in conjunction with `highlight_lines`)
337-
first.lo = sp.lo;
338-
}
317+
pub fn push_span_label(&mut self, span: Span, label: String) {
318+
self.span_labels.push((span, label));
319+
}
320+
321+
/// Selects the first primary span (if any)
322+
pub fn primary_span(&self) -> Option<Span> {
323+
self.primary_spans.first().cloned()
324+
}
325+
326+
/// Returns all primary spans.
327+
pub fn primary_spans(&self) -> &[Span] {
328+
&self.primary_spans
329+
}
330+
331+
/// Returns the strings to highlight. If we have an explicit set,
332+
/// return those, otherwise just give back an (unlabeled) version
333+
/// of the primary span.
334+
pub fn span_labels(&self) -> Vec<SpanLabel> {
335+
let is_primary = |span| self.primary_spans.contains(&span);
336+
let mut span_labels = vec![];
337+
338+
for &(span, ref label) in &self.span_labels {
339+
span_labels.push(SpanLabel {
340+
span: span,
341+
is_primary: is_primary(span),
342+
label: Some(label.clone())
343+
});
339344
}
340345

341-
for idx in 0.. {
342-
if let Some(sp_trim) = sp.trim_start(prev) {
343-
// Implies `sp.hi > prev.hi`
344-
let cur = match self.spans.get(idx) {
345-
Some(s) => *s,
346-
None => {
347-
sp = sp_trim;
348-
break;
349-
}
350-
};
351-
// `cur` may overlap with `sp_trim`
352-
if let Some(cur_trim) = cur.trim_start(sp_trim) {
353-
// Implies `sp.hi < cur.hi`
354-
self.spans.insert(idx, sp_trim);
355-
self.spans[idx + 1] = cur_trim;
356-
return;
357-
} else if sp.hi == cur.hi {
358-
return;
359-
}
360-
prev = cur;
346+
for &span in &self.primary_spans {
347+
if !span_labels.iter().any(|sl| sl.span == span) {
348+
span_labels.push(SpanLabel {
349+
span: span,
350+
is_primary: true,
351+
label: None
352+
});
361353
}
362354
}
363-
self.spans.push(sp);
355+
356+
span_labels
364357
}
365358
}
366359

367360
impl From<Span> for MultiSpan {
368361
fn from(span: Span) -> MultiSpan {
369-
MultiSpan { spans: vec![span] }
362+
MultiSpan::from_span(span)
370363
}
371364
}
372365

@@ -929,6 +922,10 @@ impl CodeMap {
929922
}
930923

931924
pub fn span_to_string(&self, sp: Span) -> String {
925+
if sp == COMMAND_LINE_SP {
926+
return "<command line option>".to_string();
927+
}
928+
932929
if self.files.borrow().is_empty() && sp.source_equal(&DUMMY_SP) {
933930
return "no-location".to_string();
934931
}
@@ -1099,12 +1096,16 @@ impl CodeMap {
10991096
}
11001097

11011098
pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
1099+
debug!("span_to_lines(sp={:?})", sp);
1100+
11021101
if sp.lo > sp.hi {
11031102
return Err(SpanLinesError::IllFormedSpan(sp));
11041103
}
11051104

11061105
let lo = self.lookup_char_pos(sp.lo);
1106+
debug!("span_to_lines: lo={:?}", lo);
11071107
let hi = self.lookup_char_pos(sp.hi);
1108+
debug!("span_to_lines: hi={:?}", hi);
11081109

11091110
if lo.file.start_pos != hi.file.start_pos {
11101111
return Err(SpanLinesError::DistinctSources(DistinctSources {
@@ -1184,59 +1185,6 @@ impl CodeMap {
11841185
}
11851186
}
11861187

1187-
/// Groups and sorts spans by lines into `MultiSpan`s, where `push` adds them to their group,
1188-
/// specifying the unification behaviour for overlapping spans.
1189-
/// Spans overflowing a line are put into their own one-element-group.
1190-
pub fn custom_group_spans<F>(&self, mut spans: Vec<Span>, push: F) -> Vec<MultiSpan>
1191-
where F: Fn(&mut MultiSpan, Span)
1192-
{
1193-
spans.sort_by(|a, b| a.lo.cmp(&b.lo));
1194-
let mut groups = Vec::<MultiSpan>::new();
1195-
let mut overflowing = vec![];
1196-
let mut prev_expn = ExpnId(!2u32);
1197-
let mut prev_file = !0usize;
1198-
let mut prev_line = !0usize;
1199-
let mut err_size = 0;
1200-
1201-
for sp in spans {
1202-
let line = self.lookup_char_pos(sp.lo).line;
1203-
let line_hi = self.lookup_char_pos(sp.hi).line;
1204-
if line != line_hi {
1205-
overflowing.push(sp.into());
1206-
continue
1207-
}
1208-
let file = self.lookup_filemap_idx(sp.lo);
1209-
1210-
if err_size < MAX_HIGHLIGHT_LINES && sp.expn_id == prev_expn && file == prev_file {
1211-
// `push` takes care of sorting, trimming, and merging
1212-
push(&mut groups.last_mut().unwrap(), sp);
1213-
if line != prev_line {
1214-
err_size += 1;
1215-
}
1216-
} else {
1217-
groups.push(sp.into());
1218-
err_size = 1;
1219-
}
1220-
prev_expn = sp.expn_id;
1221-
prev_file = file;
1222-
prev_line = line;
1223-
}
1224-
groups.extend(overflowing);
1225-
groups
1226-
}
1227-
1228-
/// Groups and sorts spans by lines into `MultiSpan`s, merging overlapping spans.
1229-
/// Spans overflowing a line are put into their own one-element-group.
1230-
pub fn group_spans(&self, spans: Vec<Span>) -> Vec<MultiSpan> {
1231-
self.custom_group_spans(spans, |msp, sp| msp.push_merge(sp))
1232-
}
1233-
1234-
/// Like `group_spans`, but trims overlapping spans instead of
1235-
/// merging them (for use with `end_highlight_lines`)
1236-
pub fn end_group_spans(&self, spans: Vec<Span>) -> Vec<MultiSpan> {
1237-
self.custom_group_spans(spans, |msp, sp| msp.push_trim(sp))
1238-
}
1239-
12401188
pub fn get_filemap(&self, filename: &str) -> Rc<FileMap> {
12411189
for fm in self.files.borrow().iter() {
12421190
if filename == fm.name {

0 commit comments

Comments
 (0)