about summary refs log tree commit diff
path: root/src/libsyntax/codemap.rs
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-04-20 14:52:31 -0400
committerNiko Matsakis <niko@alum.mit.edu>2016-05-02 11:47:09 -0400
commita20ee76b56c46a593238ce7ac9b9f70a99c43ff4 (patch)
treec912cbe4ba262d7f74d5589011848698284fa0ce /src/libsyntax/codemap.rs
parente1a575cb077d2070cc4527fa43bf9ef790f89f04 (diff)
downloadrust-a20ee76b56c46a593238ce7ac9b9f70a99c43ff4.tar.gz
rust-a20ee76b56c46a593238ce7ac9b9f70a99c43ff4.zip
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.
Diffstat (limited to 'src/libsyntax/codemap.rs')
-rw-r--r--src/libsyntax/codemap.rs218
1 files changed, 83 insertions, 135 deletions
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index 35aa827782d..228af27f4b1 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -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() }
+        MultiSpan {
+            primary_spans: vec![],
+            span_labels: vec![]
+        }
     }
 
-    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 }
+    pub fn from_span(primary_span: Span) -> MultiSpan {
+        MultiSpan {
+            primary_spans: vec![primary_span],
+            span_labels: vec![]
+        }
     }
 
-    /// 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);
+    pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
+        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 push_primary_span(&mut self, span: Span) {
+        self.primary_spans.push(span);
+    }
 
-        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 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 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;
+        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
+                });
             }
         }
-        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 {