diff options
| author | Jeffrey Seyfried <jeffrey.seyfried@gmail.com> | 2016-06-25 22:27:27 +0000 |
|---|---|---|
| committer | Jeffrey Seyfried <jeffrey.seyfried@gmail.com> | 2016-06-25 22:35:09 +0000 |
| commit | d3ae56d755f912471e4c36982a069317842fa495 (patch) | |
| tree | b2056ff0adb5634e0793e94b98af46cd19709390 /src/libsyntax | |
| parent | 4e2e31c11837f244e5039165b777ddedde5dc44d (diff) | |
| parent | bc1400600be083cfb39ffdc6f6b32153e585cd3c (diff) | |
| download | rust-d3ae56d755f912471e4c36982a069317842fa495.tar.gz rust-d3ae56d755f912471e4c36982a069317842fa495.zip | |
Rollup merge of #34403 - jonathandturner:move_liberror, r=alexcrichton
This PR refactors the 'errors' part of libsyntax into its own crate (librustc_errors). This is the first part of a few refactorings to simplify error reporting and potentially support more output formats (like a standardized JSON output and possibly an --explain mode that can work with the user's code), though this PR stands on its own and doesn't assume further changes. As part of separating out the errors crate, I have also refactored the code position portion of codemap into its own crate (libsyntax_pos). While it's helpful to have the common code positions in a separate crate for the new errors crate, this may also enable further simplifications in the future.
Diffstat (limited to 'src/libsyntax')
36 files changed, 994 insertions, 3852 deletions
diff --git a/src/libsyntax/Cargo.toml b/src/libsyntax/Cargo.toml index 964f2dcb6b6..723bb63cfe5 100644 --- a/src/libsyntax/Cargo.toml +++ b/src/libsyntax/Cargo.toml @@ -12,3 +12,4 @@ crate-type = ["dylib"] serialize = { path = "../libserialize" } log = { path = "../liblog" } rustc_bitflags = { path = "../librustc_bitflags" } +syntax_pos = { path = "../libsyntax_pos" } \ No newline at end of file diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index b2aafca40a3..ca5e9231a30 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -16,7 +16,8 @@ pub use self::ViewPath_::*; pub use self::PathParameters::*; use attr::{ThinAttributes, HasAttrs}; -use codemap::{mk_sp, respan, Span, Spanned, DUMMY_SP, ExpnId}; +use syntax_pos::{mk_sp, Span, DUMMY_SP, ExpnId}; +use codemap::{respan, Spanned}; use abi::Abi; use errors; use ext::base; diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index e36e15802f0..9f2566a381c 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -18,8 +18,8 @@ use ast; use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaItemKind}; use ast::{Stmt, StmtKind, DeclKind}; use ast::{Expr, Item, Local, Decl}; -use codemap::{Span, Spanned, spanned, dummy_spanned}; -use codemap::BytePos; +use codemap::{spanned, dummy_spanned, Spanned}; +use syntax_pos::{Span, BytePos}; use errors::Handler; use feature_gate::{Features, GatedCfg}; use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration}; diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 5e1335b45aa..743f96d737e 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -19,269 +19,18 @@ pub use self::ExpnFormat::*; -use std::cell::{Cell, RefCell}; -use std::ops::{Add, Sub}; +use std::cell::RefCell; use std::path::{Path,PathBuf}; use std::rc::Rc; -use std::cmp; use std::env; -use std::{fmt, fs}; +use std::fs; use std::io::{self, Read}; - -use serialize::{Encodable, Decodable, Encoder, Decoder}; +pub use syntax_pos::*; +use errors::CodeMapper; use ast::Name; -// _____________________________________________________________________________ -// Pos, BytePos, CharPos -// - -pub trait Pos { - fn from_usize(n: usize) -> Self; - fn to_usize(&self) -> usize; -} - -/// A byte offset. Keep this small (currently 32-bits), as AST contains -/// a lot of them. -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] -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, Ord, Debug)] -pub struct CharPos(pub usize); - -// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix -// have been unsuccessful - -impl Pos for BytePos { - fn from_usize(n: usize) -> BytePos { BytePos(n as u32) } - fn to_usize(&self) -> usize { let BytePos(n) = *self; n as usize } -} - -impl Add for BytePos { - type Output = BytePos; - - fn add(self, rhs: BytePos) -> BytePos { - BytePos((self.to_usize() + rhs.to_usize()) as u32) - } -} - -impl Sub for BytePos { - type Output = BytePos; - - fn sub(self, rhs: BytePos) -> BytePos { - BytePos((self.to_usize() - rhs.to_usize()) as u32) - } -} - -impl Encodable for BytePos { - fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_u32(self.0) - } -} - -impl Decodable for BytePos { - fn decode<D: Decoder>(d: &mut D) -> Result<BytePos, D::Error> { - Ok(BytePos(d.read_u32()?)) - } -} - -impl Pos for CharPos { - fn from_usize(n: usize) -> CharPos { CharPos(n) } - fn to_usize(&self) -> usize { let CharPos(n) = *self; n } -} - -impl Add for CharPos { - type Output = CharPos; - - fn add(self, rhs: CharPos) -> CharPos { - CharPos(self.to_usize() + rhs.to_usize()) - } -} - -impl Sub for CharPos { - type Output = CharPos; - - fn sub(self, rhs: CharPos) -> CharPos { - CharPos(self.to_usize() - rhs.to_usize()) - } -} - -// _____________________________________________________________________________ -// Span, MultiSpan, Spanned -// - -/// Spans represent a region of code, used for error reporting. Positions in spans -/// are *absolute* positions from the beginning of the codemap, not positions -/// relative to FileMaps. Methods on the CodeMap can be used to relate spans back -/// to the original source. -/// You must be careful if the span crosses more than one file - you will not be -/// able to use many of the functions on spans in codemap and you cannot assume -/// that the length of the span = hi - lo; there may be space in the BytePos -/// range between files. -#[derive(Clone, Copy, Hash, PartialEq, Eq)] -pub struct Span { - pub lo: BytePos, - pub hi: BytePos, - /// Information about where the macro came from, if this piece of - /// code was created by a macro expansion. - pub expn_id: ExpnId -} - -/// 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 { - 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 }; - -// Generic span to be used for code originating from the command line -pub const COMMAND_LINE_SP: Span = Span { lo: BytePos(0), - hi: BytePos(0), - expn_id: COMMAND_LINE_EXPN }; - -impl Span { - /// Returns a new span representing just the end-point of this span - pub fn end_point(self) -> Span { - let lo = cmp::max(self.hi.0 - 1, self.lo.0); - Span { lo: BytePos(lo), hi: self.hi, expn_id: self.expn_id} - } - - /// Returns `self` if `self` is not the dummy span, and `other` otherwise. - pub fn substitute_dummy(self, other: Span) -> Span { - if self.source_equal(&DUMMY_SP) { other } else { self } - } - - pub fn contains(self, other: Span) -> bool { - self.lo <= other.lo && other.hi <= self.hi - } - - /// Return true if the spans are equal with regards to the source text. - /// - /// Use this instead of `==` when either span could be generated code, - /// and you only care that they point to the same bytes of source text. - pub fn source_equal(&self, other: &Span) -> bool { - 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 { - Some(Span { lo: cmp::max(self.lo, other.hi), .. self }) - } else { - None - } - } -} - -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] -pub struct Spanned<T> { - pub node: T, - pub span: Span, -} - -impl Encodable for Span { - fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("Span", 2, |s| { - s.emit_struct_field("lo", 0, |s| { - self.lo.encode(s) - })?; - - s.emit_struct_field("hi", 1, |s| { - self.hi.encode(s) - }) - }) - } -} - -impl Decodable for Span { - fn decode<D: Decoder>(d: &mut D) -> Result<Span, D::Error> { - d.read_struct("Span", 2, |d| { - let lo = d.read_struct_field("lo", 0, |d| { - BytePos::decode(d) - })?; - - let hi = d.read_struct_field("hi", 1, |d| { - BytePos::decode(d) - })?; - - Ok(mk_sp(lo, hi)) - }) - } -} - -fn default_span_debug(span: Span, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Span {{ lo: {:?}, hi: {:?}, expn_id: {:?} }}", - span.lo, span.hi, span.expn_id) -} - -thread_local!(pub static SPAN_DEBUG: Cell<fn(Span, &mut fmt::Formatter) -> fmt::Result> = - Cell::new(default_span_debug)); - -impl fmt::Debug for Span { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - SPAN_DEBUG.with(|span_debug| span_debug.get()(*self, f)) - } -} - -pub fn spanned<T>(lo: BytePos, hi: BytePos, t: T) -> Spanned<T> { - respan(mk_sp(lo, hi), t) -} - -pub fn respan<T>(sp: Span, t: T) -> Spanned<T> { - Spanned {node: t, span: sp} -} - -pub fn dummy_spanned<T>(t: T) -> Spanned<T> { - respan(DUMMY_SP, t) -} - -/* assuming that we're not in macro expansion */ -pub fn mk_sp(lo: BytePos, hi: BytePos) -> Span { - Span {lo: lo, hi: hi, expn_id: NO_EXPANSION} -} - /// Return the span itself if it doesn't come from a macro expansion, /// otherwise return the call site span up to the `enclosing_sp` by /// following the `expn_info` chain. @@ -295,123 +44,31 @@ pub fn original_sp(cm: &CodeMap, sp: Span, enclosing_sp: Span) -> Span { } } -impl MultiSpan { - pub fn new() -> MultiSpan { - MultiSpan { - primary_spans: vec![], - span_labels: vec![] - } - } - - pub fn from_span(primary_span: Span) -> MultiSpan { - MultiSpan { - primary_spans: vec![primary_span], - span_labels: vec![] - } - } - - pub fn from_spans(vec: Vec<Span>) -> MultiSpan { - MultiSpan { - primary_spans: vec, - span_labels: vec![] - } - } - - 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. We always ensure that there - /// is an entry for each of the primary spans -- for each primary - /// span P, if there is at least one label with span P, we return - /// those labels (marked as primary). But otherwise we return - /// `SpanLabel` instances with empty labels. - 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 - }); - } - } - - span_labels - } +/// The source of expansion. +#[derive(Clone, Hash, Debug, PartialEq, Eq)] +pub enum ExpnFormat { + /// e.g. #[derive(...)] <item> + MacroAttribute(Name), + /// e.g. `format!()` + MacroBang(Name), } -impl From<Span> for MultiSpan { - fn from(span: Span) -> MultiSpan { - MultiSpan::from_span(span) - } +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +pub struct Spanned<T> { + pub node: T, + pub span: Span, } -// _____________________________________________________________________________ -// Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos -// - -/// A source code location used for error reporting -#[derive(Debug)] -pub struct Loc { - /// Information about the original source - pub file: Rc<FileMap>, - /// The (1-based) line number - pub line: usize, - /// The (0-based) column offset - pub col: CharPos +pub fn spanned<T>(lo: BytePos, hi: BytePos, t: T) -> Spanned<T> { + respan(mk_sp(lo, hi), t) } -/// A source code location used as the result of lookup_char_pos_adj -// Actually, *none* of the clients use the filename *or* file field; -// perhaps they should just be removed. -#[derive(Debug)] -pub struct LocWithOpt { - pub filename: FileName, - pub line: usize, - pub col: CharPos, - pub file: Option<Rc<FileMap>>, +pub fn respan<T>(sp: Span, t: T) -> Spanned<T> { + Spanned {node: t, span: sp} } -// used to be structural records. Better names, anyone? -#[derive(Debug)] -pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize } -#[derive(Debug)] -pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos } - - -// _____________________________________________________________________________ -// ExpnFormat, NameAndSpan, ExpnInfo, ExpnId -// - -/// The source of expansion. -#[derive(Clone, Hash, Debug, PartialEq, Eq)] -pub enum ExpnFormat { - /// e.g. #[derive(...)] <item> - MacroAttribute(Name), - /// e.g. `format!()` - MacroBang(Name), +pub fn dummy_spanned<T>(t: T) -> Spanned<T> { + respan(DUMMY_SP, t) } #[derive(Clone, Hash, Debug)] @@ -454,257 +111,10 @@ pub struct ExpnInfo { pub callee: NameAndSpan } -#[derive(PartialEq, Eq, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Copy)] -pub struct ExpnId(u32); - -pub const NO_EXPANSION: ExpnId = ExpnId(!0); -// For code appearing from the command line -pub const COMMAND_LINE_EXPN: ExpnId = ExpnId(!1); - -impl ExpnId { - pub fn from_u32(id: u32) -> ExpnId { - ExpnId(id) - } - - pub fn into_u32(self) -> u32 { - self.0 - } -} - // _____________________________________________________________________________ // FileMap, MultiByteChar, FileName, FileLines // -pub type FileName = String; - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct LineInfo { - /// Index of line, starting from 0. - pub line_index: usize, - - /// Column in line where span begins, starting from 0. - pub start_col: CharPos, - - /// Column in line where span ends, starting from 0, exclusive. - pub end_col: CharPos, -} - -pub struct FileLines { - pub file: Rc<FileMap>, - pub lines: Vec<LineInfo> -} - -/// Identifies an offset of a multi-byte character in a FileMap -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)] -pub struct MultiByteChar { - /// The absolute offset of the character in the CodeMap - pub pos: BytePos, - /// The number of bytes, >=2 - pub bytes: usize, -} - -/// A single source in the CodeMap. -pub struct FileMap { - /// The name of the file that the source came from, source that doesn't - /// originate from files has names between angle brackets by convention, - /// e.g. `<anon>` - pub name: FileName, - /// The absolute path of the file that the source came from. - pub abs_path: Option<FileName>, - /// The complete source code - pub src: Option<Rc<String>>, - /// The start position of this source in the CodeMap - pub start_pos: BytePos, - /// The end position of this source in the CodeMap - pub end_pos: BytePos, - /// Locations of lines beginnings in the source code - pub lines: RefCell<Vec<BytePos>>, - /// Locations of multi-byte characters in the source code - pub multibyte_chars: RefCell<Vec<MultiByteChar>>, -} - -impl Encodable for FileMap { - fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("FileMap", 6, |s| { - s.emit_struct_field("name", 0, |s| self.name.encode(s))?; - s.emit_struct_field("abs_path", 1, |s| self.abs_path.encode(s))?; - s.emit_struct_field("start_pos", 2, |s| self.start_pos.encode(s))?; - s.emit_struct_field("end_pos", 3, |s| self.end_pos.encode(s))?; - s.emit_struct_field("lines", 4, |s| { - let lines = self.lines.borrow(); - // store the length - s.emit_u32(lines.len() as u32)?; - - if !lines.is_empty() { - // In order to preserve some space, we exploit the fact that - // the lines list is sorted and individual lines are - // probably not that long. Because of that we can store lines - // as a difference list, using as little space as possible - // for the differences. - let max_line_length = if lines.len() == 1 { - 0 - } else { - lines.windows(2) - .map(|w| w[1] - w[0]) - .map(|bp| bp.to_usize()) - .max() - .unwrap() - }; - - let bytes_per_diff: u8 = match max_line_length { - 0 ... 0xFF => 1, - 0x100 ... 0xFFFF => 2, - _ => 4 - }; - - // Encode the number of bytes used per diff. - bytes_per_diff.encode(s)?; - - // Encode the first element. - lines[0].encode(s)?; - - let diff_iter = (&lines[..]).windows(2) - .map(|w| (w[1] - w[0])); - - match bytes_per_diff { - 1 => for diff in diff_iter { (diff.0 as u8).encode(s)? }, - 2 => for diff in diff_iter { (diff.0 as u16).encode(s)? }, - 4 => for diff in diff_iter { diff.0.encode(s)? }, - _ => unreachable!() - } - } - - Ok(()) - })?; - s.emit_struct_field("multibyte_chars", 5, |s| { - (*self.multibyte_chars.borrow()).encode(s) - }) - }) - } -} - -impl Decodable for FileMap { - fn decode<D: Decoder>(d: &mut D) -> Result<FileMap, D::Error> { - - d.read_struct("FileMap", 6, |d| { - let name: String = d.read_struct_field("name", 0, |d| Decodable::decode(d))?; - let abs_path: Option<String> = - d.read_struct_field("abs_path", 1, |d| Decodable::decode(d))?; - let start_pos: BytePos = d.read_struct_field("start_pos", 2, |d| Decodable::decode(d))?; - let end_pos: BytePos = d.read_struct_field("end_pos", 3, |d| Decodable::decode(d))?; - let lines: Vec<BytePos> = d.read_struct_field("lines", 4, |d| { - let num_lines: u32 = Decodable::decode(d)?; - let mut lines = Vec::with_capacity(num_lines as usize); - - if num_lines > 0 { - // Read the number of bytes used per diff. - let bytes_per_diff: u8 = Decodable::decode(d)?; - - // Read the first element. - let mut line_start: BytePos = Decodable::decode(d)?; - lines.push(line_start); - - for _ in 1..num_lines { - let diff = match bytes_per_diff { - 1 => d.read_u8()? as u32, - 2 => d.read_u16()? as u32, - 4 => d.read_u32()?, - _ => unreachable!() - }; - - line_start = line_start + BytePos(diff); - - lines.push(line_start); - } - } - - Ok(lines) - })?; - let multibyte_chars: Vec<MultiByteChar> = - d.read_struct_field("multibyte_chars", 5, |d| Decodable::decode(d))?; - Ok(FileMap { - name: name, - abs_path: abs_path, - start_pos: start_pos, - end_pos: end_pos, - src: None, - lines: RefCell::new(lines), - multibyte_chars: RefCell::new(multibyte_chars) - }) - }) - } -} - -impl fmt::Debug for FileMap { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "FileMap({})", self.name) - } -} - -impl FileMap { - /// EFFECT: register a start-of-line offset in the - /// table of line-beginnings. - /// UNCHECKED INVARIANT: these offsets must be added in the right - /// order and must be in the right places; there is shared knowledge - /// about what ends a line between this file and parse.rs - /// WARNING: pos param here is the offset relative to start of CodeMap, - /// and CodeMap will append a newline when adding a filemap without a newline at the end, - /// so the safe way to call this is with value calculated as - /// filemap.start_pos + newline_offset_relative_to_the_start_of_filemap. - pub fn next_line(&self, pos: BytePos) { - // the new charpos must be > the last one (or it's the first one). - let mut lines = self.lines.borrow_mut(); - let line_len = lines.len(); - assert!(line_len == 0 || ((*lines)[line_len - 1] < pos)); - lines.push(pos); - } - - /// get a line from the list of pre-computed line-beginnings. - /// line-number here is 0-based. - pub fn get_line(&self, line_number: usize) -> Option<&str> { - match self.src { - Some(ref src) => { - let lines = self.lines.borrow(); - lines.get(line_number).map(|&line| { - let begin: BytePos = line - self.start_pos; - let begin = begin.to_usize(); - // We can't use `lines.get(line_number+1)` because we might - // be parsing when we call this function and thus the current - // line is the last one we have line info for. - let slice = &src[begin..]; - match slice.find('\n') { - Some(e) => &slice[..e], - None => slice - } - }) - } - None => None - } - } - - pub fn record_multibyte_char(&self, pos: BytePos, bytes: usize) { - assert!(bytes >=2 && bytes <= 4); - let mbc = MultiByteChar { - pos: pos, - bytes: bytes, - }; - self.multibyte_chars.borrow_mut().push(mbc); - } - - pub fn is_real_file(&self) -> bool { - !(self.name.starts_with("<") && - self.name.ends_with(">")) - } - - pub fn is_imported(&self) -> bool { - self.src.is_none() - } - - fn count_lines(&self) -> usize { - self.lines.borrow().len() - } -} - /// An abstraction over the fs operations used by the Parser. pub trait FileLoader { /// Query the existence of a file. @@ -1392,52 +802,24 @@ impl CodeMap { } } -pub struct MacroBacktrace { - /// span where macro was applied to generate this code - pub call_site: Span, - - /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]") - pub macro_decl_name: String, - - /// span where macro was defined (if known) - pub def_site_span: Option<Span>, -} - -// _____________________________________________________________________________ -// SpanLinesError, SpanSnippetError, DistinctSources, MalformedCodemapPositions -// - -pub type FileLinesResult = Result<FileLines, SpanLinesError>; - -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum SpanLinesError { - IllFormedSpan(Span), - DistinctSources(DistinctSources), -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum SpanSnippetError { - IllFormedSpan(Span), - DistinctSources(DistinctSources), - MalformedForCodemap(MalformedCodemapPositions), - SourceNotAvailable { filename: String } -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct DistinctSources { - begin: (String, BytePos), - end: (String, BytePos) -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct MalformedCodemapPositions { - name: String, - source_len: usize, - begin_pos: BytePos, - end_pos: BytePos +impl CodeMapper for CodeMap { + fn lookup_char_pos(&self, pos: BytePos) -> Loc { + self.lookup_char_pos(pos) + } + fn span_to_lines(&self, sp: Span) -> FileLinesResult { + self.span_to_lines(sp) + } + fn span_to_string(&self, sp: Span) -> String { + self.span_to_string(sp) + } + fn span_to_filename(&self, sp: Span) -> FileName { + self.span_to_filename(sp) + } + fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace> { + self.macro_backtrace(span) + } } - // _____________________________________________________________________________ // Tests // @@ -1445,6 +827,13 @@ pub struct MalformedCodemapPositions { #[cfg(test)] mod tests { use super::*; + use errors::{Level, CodeSuggestion}; + use errors::emitter::EmitterWriter; + use errors::snippet::{SnippetData, RenderedLine, FormatMode}; + use std::sync::{Arc, Mutex}; + use std::io::{self, Write}; + use std::str::from_utf8; + use std::rc::Rc; #[test] fn t1 () { @@ -1688,6 +1077,69 @@ mod tests { blork.rs:1:1: 1:12\n `first line.`\n"); } + /// Returns the span corresponding to the `n`th occurrence of + /// `substring` in `source_text`. + trait CodeMapExtension { + fn span_substr(&self, + file: &Rc<FileMap>, + source_text: &str, + substring: &str, + n: usize) + -> Span; + } + + impl CodeMapExtension for CodeMap { + fn span_substr(&self, + file: &Rc<FileMap>, + source_text: &str, + substring: &str, + n: usize) + -> Span + { + println!("span_substr(file={:?}/{:?}, substring={:?}, n={})", + file.name, file.start_pos, substring, n); + let mut i = 0; + let mut hi = 0; + loop { + let offset = source_text[hi..].find(substring).unwrap_or_else(|| { + panic!("source_text `{}` does not have {} occurrences of `{}`, only {}", + source_text, n, substring, i); + }); + let lo = hi + offset; + hi = lo + substring.len(); + if i == n { + let span = Span { + lo: BytePos(lo as u32 + file.start_pos.0), + hi: BytePos(hi as u32 + file.start_pos.0), + expn_id: NO_EXPANSION, + }; + assert_eq!(&self.span_to_snippet(span).unwrap()[..], + substring); + return span; + } + i += 1; + } + } + } + + fn splice(start: Span, end: Span) -> Span { + Span { + lo: start.lo, + hi: end.hi, + expn_id: NO_EXPANSION, + } + } + + fn make_string(lines: &[RenderedLine]) -> String { + lines.iter() + .flat_map(|rl| { + rl.text.iter() + .map(|s| &s.text[..]) + .chain(Some("\n")) + }) + .collect() + } + fn init_expansion_chain(cm: &CodeMap) -> Span { // Creates an expansion chain containing two recursive calls // root -> expA -> expA -> expB -> expB -> end @@ -1767,4 +1219,761 @@ r"blork2.rs:2:1: 2:12 "; assert_eq!(sstr, res_str); } + + struct Sink(Arc<Mutex<Vec<u8>>>); + impl Write for Sink { + fn write(&mut self, data: &[u8]) -> io::Result<usize> { + Write::write(&mut *self.0.lock().unwrap(), data) + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } + } + + // Diagnostic doesn't align properly in span where line number increases by one digit + #[test] + fn test_hilight_suggestion_issue_11715() { + let data = Arc::new(Mutex::new(Vec::new())); + let cm = Rc::new(CodeMap::new()); + let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), + None, + cm.clone(), + FormatMode::NewErrorFormat); + let content = "abcdefg + koksi + line3 + line4 + cinq + line6 + line7 + line8 + line9 + line10 + e-lä-vän + tolv + dreizehn + "; + let file = cm.new_filemap_and_lines("dummy.txt", None, content); + let start = file.lines.borrow()[10]; + let end = file.lines.borrow()[11]; + let sp = mk_sp(start, end); + let lvl = Level::Error; + println!("highlight_lines"); + ew.highlight_lines(&sp.into(), lvl).unwrap(); + println!("done"); + let vec = data.lock().unwrap().clone(); + let vec: &[u8] = &vec; + let str = from_utf8(vec).unwrap(); + println!("r#\"\n{}\"#", str); + assert_eq!(str, &r#" + --> dummy.txt:11:1 + |> +11 |> e-lä-vän + |> ^ +"#[1..]); + } + + #[test] + fn test_single_span_splice() { + // Test that a `MultiSpan` containing a single span splices a substition correctly + let cm = CodeMap::new(); + let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; + let selection = " \n ~~\n~~~\n~~~~~ \n \n"; + cm.new_filemap_and_lines("blork.rs", None, inputtext); + let sp = span_from_selection(inputtext, selection); + let msp: MultiSpan = sp.into(); + + // check that we are extracting the text we thought we were extracting + assert_eq!(&cm.span_to_snippet(sp).unwrap(), "BB\nCCC\nDDDDD"); + + let substitute = "ZZZZZZ".to_owned(); + let expected = "bbbbZZZZZZddddd"; + let suggest = CodeSuggestion { + msp: msp, + substitutes: vec![substitute], + }; + assert_eq!(suggest.splice_lines(&cm), expected); + } + + #[test] + fn test_multi_span_splice() { + // Test that a `MultiSpan` containing multiple spans splices a substition correctly + let cm = CodeMap::new(); + let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; + let selection1 = " \n \n \n \n ~ \n"; // intentionally out of order + let selection2 = " \n ~~\n~~~\n~~~~~ \n \n"; + cm.new_filemap_and_lines("blork.rs", None, inputtext); + let sp1 = span_from_selection(inputtext, selection1); + let sp2 = span_from_selection(inputtext, selection2); + let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]); + + let expected = "bbbbZZZZZZddddd\neXYZe"; + let suggest = CodeSuggestion { + msp: msp, + substitutes: vec!["ZZZZZZ".to_owned(), + "XYZ".to_owned()] + }; + + assert_eq!(suggest.splice_lines(&cm), expected); + } + + #[test] + fn test_multispan_highlight() { + let data = Arc::new(Mutex::new(Vec::new())); + let cm = Rc::new(CodeMap::new()); + let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), + None, + cm.clone(), + FormatMode::NewErrorFormat); + + let inp = "_____aaaaaa____bbbbbb__cccccdd_"; + let sp1 = " ~~~~~~ "; + let sp2 = " ~~~~~~ "; + let sp3 = " ~~~~~ "; + let sp4 = " ~~~~ "; + let sp34 = " ~~~~~~~ "; + + let expect_start = &r#" + --> dummy.txt:1:6 + |> +1 |> _____aaaaaa____bbbbbb__cccccdd_ + |> ^^^^^^ ^^^^^^ ^^^^^^^ +"#[1..]; + + let span = |sp, expected| { + let sp = span_from_selection(inp, sp); + assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected); + sp + }; + cm.new_filemap_and_lines("dummy.txt", None, inp); + let sp1 = span(sp1, "aaaaaa"); + let sp2 = span(sp2, "bbbbbb"); + let sp3 = span(sp3, "ccccc"); + let sp4 = span(sp4, "ccdd"); + let sp34 = span(sp34, "cccccdd"); + + let spans = vec![sp1, sp2, sp3, sp4]; + + let test = |expected, highlight: &mut FnMut()| { + data.lock().unwrap().clear(); + highlight(); + let vec = data.lock().unwrap().clone(); + let actual = from_utf8(&vec[..]).unwrap(); + println!("actual=\n{}", actual); + assert_eq!(actual, expected); + }; + + let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]); + test(expect_start, &mut || { + diag.highlight_lines(&msp, Level::Error).unwrap(); + }); + test(expect_start, &mut || { + let msp = MultiSpan::from_spans(spans.clone()); + diag.highlight_lines(&msp, Level::Error).unwrap(); + }); + } + + #[test] + fn test_huge_multispan_highlight() { + let data = Arc::new(Mutex::new(Vec::new())); + let cm = Rc::new(CodeMap::new()); + let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), + None, + cm.clone(), + FormatMode::NewErrorFormat); + + let inp = "aaaaa\n\ + aaaaa\n\ + aaaaa\n\ + bbbbb\n\ + ccccc\n\ + xxxxx\n\ + yyyyy\n\ + _____\n\ + ddd__eee_\n\ + elided\n\ + __f_gg"; + let file = cm.new_filemap_and_lines("dummy.txt", None, inp); + + let span = |lo, hi, (off_lo, off_hi)| { + let lines = file.lines.borrow(); + let (mut lo, mut hi): (BytePos, BytePos) = (lines[lo], lines[hi]); + lo.0 += off_lo; + hi.0 += off_hi; + mk_sp(lo, hi) + }; + let sp0 = span(4, 6, (0, 5)); + let sp1 = span(0, 6, (0, 5)); + let sp2 = span(8, 8, (0, 3)); + let sp3 = span(8, 8, (5, 8)); + let sp4 = span(10, 10, (2, 3)); + let sp5 = span(10, 10, (4, 6)); + + let expect0 = &r#" + --> dummy.txt:5:1 + |> +5 |> ccccc + |> ^ +... +9 |> ddd__eee_ + |> ^^^ ^^^ +10 |> elided +11 |> __f_gg + |> ^ ^^ +"#[1..]; + + let expect = &r#" + --> dummy.txt:1:1 + |> +1 |> aaaaa + |> ^ +... +9 |> ddd__eee_ + |> ^^^ ^^^ +10 |> elided +11 |> __f_gg + |> ^ ^^ +"#[1..]; + + macro_rules! test { + ($expected: expr, $highlight: expr) => ({ + data.lock().unwrap().clear(); + $highlight(); + let vec = data.lock().unwrap().clone(); + let actual = from_utf8(&vec[..]).unwrap(); + println!("actual:"); + println!("{}", actual); + println!("expected:"); + println!("{}", $expected); + assert_eq!(&actual[..], &$expected[..]); + }); + } + + let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]); + let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]); + + test!(expect0, || { + diag.highlight_lines(&msp0, Level::Error).unwrap(); + }); + test!(expect, || { + diag.highlight_lines(&msp, Level::Error).unwrap(); + }); + } + + #[test] + fn tab() { + let file_text = " +fn foo() { +\tbar; +} +"; + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + let span_bar = cm.span_substr(&foo, file_text, "bar", 0); + + let mut snippet = SnippetData::new(cm, Some(span_bar), FormatMode::NewErrorFormat); + snippet.push(span_bar, true, None); + + let lines = snippet.render_lines(); + let text = make_string(&lines); + assert_eq!(&text[..], &" + --> foo.rs:3:2 + |> +3 |> \tbar; + |> \t^^^ +"[1..]); + } + + #[test] + fn one_line() { + let file_text = r#" +fn foo() { + vec.push(vec.pop().unwrap()); +} +"#; + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0); + let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1); + let span_semi = cm.span_substr(&foo, file_text, ";", 0); + + let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat); + snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here"))); + snippet.push(span_vec1, false, Some(format!("error occurs here"))); + snippet.push(span_semi, false, Some(format!("previous borrow ends here"))); + + let lines = snippet.render_lines(); + println!("{:#?}", lines); + + let text: String = make_string(&lines); + + println!("text=\n{}", text); + assert_eq!(&text[..], &r#" + ::: foo.rs + |> +3 |> vec.push(vec.pop().unwrap()); + |> --- --- - previous borrow ends here + |> | | + |> | error occurs here + |> previous borrow of `vec` occurs here +"#[1..]); + } + + #[test] + fn two_files() { + let file_text_foo = r#" +fn foo() { + vec.push(vec.pop().unwrap()); +} +"#; + + let file_text_bar = r#" +fn bar() { + // these blank links here + // serve to ensure that the line numbers + // from bar.rs + // require more digits + + + + + + + + + + + vec.push(); + + // this line will get elided + + vec.pop().unwrap()); +} +"#; + + let cm = Rc::new(CodeMap::new()); + let foo_map = cm.new_filemap_and_lines("foo.rs", None, file_text_foo); + let span_foo_vec0 = cm.span_substr(&foo_map, file_text_foo, "vec", 0); + let span_foo_vec1 = cm.span_substr(&foo_map, file_text_foo, "vec", 1); + let span_foo_semi = cm.span_substr(&foo_map, file_text_foo, ";", 0); + + let bar_map = cm.new_filemap_and_lines("bar.rs", None, file_text_bar); + let span_bar_vec0 = cm.span_substr(&bar_map, file_text_bar, "vec", 0); + let span_bar_vec1 = cm.span_substr(&bar_map, file_text_bar, "vec", 1); + let span_bar_semi = cm.span_substr(&bar_map, file_text_bar, ";", 0); + + let mut snippet = SnippetData::new(cm, Some(span_foo_vec1), FormatMode::NewErrorFormat); + snippet.push(span_foo_vec0, false, Some(format!("a"))); + snippet.push(span_foo_vec1, true, Some(format!("b"))); + snippet.push(span_foo_semi, false, Some(format!("c"))); + snippet.push(span_bar_vec0, false, Some(format!("d"))); + snippet.push(span_bar_vec1, false, Some(format!("e"))); + snippet.push(span_bar_semi, false, Some(format!("f"))); + + let lines = snippet.render_lines(); + println!("{:#?}", lines); + + let text: String = make_string(&lines); + + println!("text=\n{}", text); + + // Note that the `|>` remain aligned across both files: + assert_eq!(&text[..], &r#" + --> foo.rs:3:14 + |> +3 |> vec.push(vec.pop().unwrap()); + |> --- ^^^ - c + |> | | + |> | b + |> a + ::: bar.rs + |> +17 |> vec.push(); + |> --- - f + |> | + |> d +... +21 |> vec.pop().unwrap()); + |> --- e +"#[1..]); + } + + #[test] + fn multi_line() { + let file_text = r#" +fn foo() { + let name = find_id(&data, 22).unwrap(); + + // Add one more item we forgot to the vector. Silly us. + data.push(Data { name: format!("Hera"), id: 66 }); + + // Print everything out. + println!("Name: {:?}", name); + println!("Data: {:?}", data); +} +"#; + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + let span_data0 = cm.span_substr(&foo, file_text, "data", 0); + let span_data1 = cm.span_substr(&foo, file_text, "data", 1); + let span_rbrace = cm.span_substr(&foo, file_text, "}", 3); + + let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat); + snippet.push(span_data0, false, Some(format!("immutable borrow begins here"))); + snippet.push(span_data1, false, Some(format!("mutable borrow occurs here"))); + snippet.push(span_rbrace, false, Some(format!("immutable borrow ends here"))); + + let lines = snippet.render_lines(); + println!("{:#?}", lines); + + let text: String = make_string(&lines); + + println!("text=\n{}", text); + assert_eq!(&text[..], &r#" + ::: foo.rs + |> +3 |> let name = find_id(&data, 22).unwrap(); + |> ---- immutable borrow begins here +... +6 |> data.push(Data { name: format!("Hera"), id: 66 }); + |> ---- mutable borrow occurs here +... +11 |> } + |> - immutable borrow ends here +"#[1..]); + } + + #[test] + fn overlapping() { + let file_text = r#" +fn foo() { + vec.push(vec.pop().unwrap()); +} +"#; + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + let span0 = cm.span_substr(&foo, file_text, "vec.push", 0); + let span1 = cm.span_substr(&foo, file_text, "vec", 0); + let span2 = cm.span_substr(&foo, file_text, "ec.push", 0); + let span3 = cm.span_substr(&foo, file_text, "unwrap", 0); + + let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat); + snippet.push(span0, false, Some(format!("A"))); + snippet.push(span1, false, Some(format!("B"))); + snippet.push(span2, false, Some(format!("C"))); + snippet.push(span3, false, Some(format!("D"))); + + let lines = snippet.render_lines(); + println!("{:#?}", lines); + let text: String = make_string(&lines); + + println!("text=r#\"\n{}\".trim_left()", text); + assert_eq!(&text[..], &r#" + ::: foo.rs + |> +3 |> vec.push(vec.pop().unwrap()); + |> -------- ------ D + |> || + |> |C + |> A + |> B +"#[1..]); + } + + #[test] + fn one_line_out_of_order() { + let file_text = r#" +fn foo() { + vec.push(vec.pop().unwrap()); +} +"#; + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0); + let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1); + let span_semi = cm.span_substr(&foo, file_text, ";", 0); + + // intentionally don't push the snippets left to right + let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat); + snippet.push(span_vec1, false, Some(format!("error occurs here"))); + snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here"))); + snippet.push(span_semi, false, Some(format!("previous borrow ends here"))); + + let lines = snippet.render_lines(); + println!("{:#?}", lines); + let text: String = make_string(&lines); + + println!("text=r#\"\n{}\".trim_left()", text); + assert_eq!(&text[..], &r#" + ::: foo.rs + |> +3 |> vec.push(vec.pop().unwrap()); + |> --- --- - previous borrow ends here + |> | | + |> | error occurs here + |> previous borrow of `vec` occurs here +"#[1..]); + } + + #[test] + fn elide_unnecessary_lines() { + let file_text = r#" +fn foo() { + let mut vec = vec![0, 1, 2]; + let mut vec2 = vec; + vec2.push(3); + vec2.push(4); + vec2.push(5); + vec2.push(6); + vec.push(7); +} +"#; + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + let span_vec0 = cm.span_substr(&foo, file_text, "vec", 3); + let span_vec1 = cm.span_substr(&foo, file_text, "vec", 8); + + let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat); + snippet.push(span_vec0, false, Some(format!("`vec` moved here because it \ + has type `collections::vec::Vec<i32>`"))); + snippet.push(span_vec1, false, Some(format!("use of moved value: `vec`"))); + + let lines = snippet.render_lines(); + println!("{:#?}", lines); + let text: String = make_string(&lines); + println!("text=r#\"\n{}\".trim_left()", text); + assert_eq!(&text[..], &r#" + ::: foo.rs + |> +4 |> let mut vec2 = vec; + |> --- `vec` moved here because it has type `collections::vec::Vec<i32>` +... +9 |> vec.push(7); + |> --- use of moved value: `vec` +"#[1..]); + } + + #[test] + fn spans_without_labels() { + let file_text = r#" +fn foo() { + let mut vec = vec![0, 1, 2]; + let mut vec2 = vec; + vec2.push(3); + vec2.push(4); + vec2.push(5); + vec2.push(6); + vec.push(7); +} +"#; + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + + let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat); + for i in 0..4 { + let span_veci = cm.span_substr(&foo, file_text, "vec", i); + snippet.push(span_veci, false, None); + } + + let lines = snippet.render_lines(); + let text: String = make_string(&lines); + println!("text=&r#\"\n{}\n\"#[1..]", text); + assert_eq!(text, &r#" + ::: foo.rs + |> +3 |> let mut vec = vec![0, 1, 2]; + |> --- --- +4 |> let mut vec2 = vec; + |> --- --- +"#[1..]); + } + + #[test] + fn span_long_selection() { + let file_text = r#" +impl SomeTrait for () { + fn foo(x: u32) { + // impl 1 + // impl 2 + // impl 3 + } +} +"#; + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + + let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat); + let fn_span = cm.span_substr(&foo, file_text, "fn", 0); + let rbrace_span = cm.span_substr(&foo, file_text, "}", 0); + snippet.push(splice(fn_span, rbrace_span), false, None); + let lines = snippet.render_lines(); + let text: String = make_string(&lines); + println!("r#\"\n{}\"", text); + assert_eq!(text, &r#" + ::: foo.rs + |> +3 |> fn foo(x: u32) { + |> - +"#[1..]); + } + + #[test] + fn span_overlap_label() { + // Test that we don't put `x_span` to the right of its highlight, + // since there is another highlight that overlaps it. + + let file_text = r#" + fn foo(x: u32) { + } +} +"#; + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + + let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat); + let fn_span = cm.span_substr(&foo, file_text, "fn foo(x: u32)", 0); + let x_span = cm.span_substr(&foo, file_text, "x", 0); + snippet.push(fn_span, false, Some(format!("fn_span"))); + snippet.push(x_span, false, Some(format!("x_span"))); + let lines = snippet.render_lines(); + let text: String = make_string(&lines); + println!("r#\"\n{}\"", text); + assert_eq!(text, &r#" + ::: foo.rs + |> +2 |> fn foo(x: u32) { + |> -------------- + |> | | + |> | x_span + |> fn_span +"#[1..]); + } + + #[test] + fn span_overlap_label2() { + // Test that we don't put `x_span` to the right of its highlight, + // since there is another highlight that overlaps it. In this + // case, the overlap is only at the beginning, but it's still + // better to show the beginning more clearly. + + let file_text = r#" + fn foo(x: u32) { + } +} +"#; + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + + let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat); + let fn_span = cm.span_substr(&foo, file_text, "fn foo(x", 0); + let x_span = cm.span_substr(&foo, file_text, "x: u32)", 0); + snippet.push(fn_span, false, Some(format!("fn_span"))); + snippet.push(x_span, false, Some(format!("x_span"))); + let lines = snippet.render_lines(); + let text: String = make_string(&lines); + println!("r#\"\n{}\"", text); + assert_eq!(text, &r#" + ::: foo.rs + |> +2 |> fn foo(x: u32) { + |> -------------- + |> | | + |> | x_span + |> fn_span +"#[1..]); + } + + #[test] + fn span_overlap_label3() { + // Test that we don't put `x_span` to the right of its highlight, + // since there is another highlight that overlaps it. In this + // case, the overlap is only at the beginning, but it's still + // better to show the beginning more clearly. + + let file_text = r#" + fn foo() { + let closure = || { + inner + }; + } +} +"#; + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + + let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat); + + let closure_span = { + let closure_start_span = cm.span_substr(&foo, file_text, "||", 0); + let closure_end_span = cm.span_substr(&foo, file_text, "}", 0); + splice(closure_start_span, closure_end_span) + }; + + let inner_span = cm.span_substr(&foo, file_text, "inner", 0); + + snippet.push(closure_span, false, Some(format!("foo"))); + snippet.push(inner_span, false, Some(format!("bar"))); + + let lines = snippet.render_lines(); + let text: String = make_string(&lines); + println!("r#\"\n{}\"", text); + assert_eq!(text, &r#" + ::: foo.rs + |> +3 |> let closure = || { + |> - foo +4 |> inner + |> ----- bar +"#[1..]); + } + + #[test] + fn span_empty() { + // In one of the unit tests, we found that the parser sometimes + // gives empty spans, and in particular it supplied an EOF span + // like this one, which points at the very end. We want to + // fallback gracefully in this case. + + let file_text = r#" +fn main() { + struct Foo; + + impl !Sync for Foo {} + + unsafe impl Send for &'static Foo { + // error: cross-crate traits with a default impl, like `core::marker::Send`, + // can only be implemented for a struct/enum type, not + // `&'static Foo` +}"#; + + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); + + let mut rbrace_span = cm.span_substr(&foo, file_text, "}", 1); + rbrace_span.lo = rbrace_span.hi; + + let mut snippet = SnippetData::new(cm.clone(), + Some(rbrace_span), + FormatMode::NewErrorFormat); + snippet.push(rbrace_span, false, None); + let lines = snippet.render_lines(); + let text: String = make_string(&lines); + println!("r#\"\n{}\"", text); + assert_eq!(text, &r#" + --> foo.rs:11:2 + |> +11 |> } + |> - +"#[1..]); + } } diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 0e5d6841c82..cc5a68eae3b 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -11,7 +11,8 @@ use attr::{AttrMetaMethods, HasAttrs}; use feature_gate::{emit_feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue}; use fold::Folder; -use {ast, fold, attr}; +use {fold, attr}; +use ast; use codemap::{Spanned, respan}; use parse::{ParseSess, token}; use ptr::P; diff --git a/src/libsyntax/diagnostics/metadata.rs b/src/libsyntax/diagnostics/metadata.rs index 181b32594f1..5bbd18bd9ee 100644 --- a/src/libsyntax/diagnostics/metadata.rs +++ b/src/libsyntax/diagnostics/metadata.rs @@ -20,7 +20,7 @@ use std::io::Write; use std::error::Error; use rustc_serialize::json::as_json; -use codemap::Span; +use syntax_pos::Span; use ext::base::ExtCtxt; use diagnostics::plugin::{ErrorMap, ErrorInfo}; diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs index 26088b1242e..abf2b39d5c6 100644 --- a/src/libsyntax/diagnostics/plugin.rs +++ b/src/libsyntax/diagnostics/plugin.rs @@ -14,7 +14,7 @@ use std::env; use ast; use ast::{Ident, Name, TokenTree}; -use codemap::Span; +use syntax_pos::Span; use ext::base::{ExtCtxt, MacEager, MacResult}; use ext::build::AstBuilder; use parse::token; @@ -23,6 +23,8 @@ use util::small_vector::SmallVector; use diagnostics::metadata::output_metadata; +pub use errors::*; + // Maximum width of any line in an extended error description (inclusive). const MAX_DESCRIPTION_WIDTH: usize = 80; diff --git a/src/libsyntax/diagnostics/registry.rs b/src/libsyntax/diagnostics/registry.rs deleted file mode 100644 index a6cfd1a5a9a..00000000000 --- a/src/libsyntax/diagnostics/registry.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::collections::HashMap; - -#[derive(Clone)] -pub struct Registry { - descriptions: HashMap<&'static str, &'static str> -} - -impl Registry { - pub fn new(descriptions: &[(&'static str, &'static str)]) -> Registry { - Registry { descriptions: descriptions.iter().cloned().collect() } - } - - pub fn find_description(&self, code: &str) -> Option<&'static str> { - self.descriptions.get(code).cloned() - } -} diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs deleted file mode 100644 index 71a03e846a2..00000000000 --- a/src/libsyntax/errors/emitter.rs +++ /dev/null @@ -1,871 +0,0 @@ -// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use self::Destination::*; - -use codemap::{self, COMMAND_LINE_SP, DUMMY_SP, Span, MultiSpan}; -use diagnostics; - -use errors::check_old_skool; -use errors::{Level, RenderSpan, CodeSuggestion, DiagnosticBuilder}; -use errors::RenderSpan::*; -use errors::Level::*; -use errors::snippet::{RenderedLineKind, SnippetData, Style}; - -use std::{cmp, fmt}; -use std::io::prelude::*; -use std::io; -use std::rc::Rc; -use term; - -/// Emitter trait for emitting errors. Do not implement this directly: -/// implement `CoreEmitter` instead. -pub trait Emitter { - /// Emit a standalone diagnostic message. - fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, lvl: Level); - - /// Emit a structured diagnostic. - fn emit_struct(&mut self, db: &DiagnosticBuilder); -} - -pub trait CoreEmitter { - fn emit_message(&mut self, - rsp: &RenderSpan, - msg: &str, - code: Option<&str>, - lvl: Level, - is_header: bool, - show_snippet: bool); -} - -impl<T: CoreEmitter> Emitter for T { - fn emit(&mut self, - msp: &MultiSpan, - msg: &str, - code: Option<&str>, - lvl: Level) { - self.emit_message(&FullSpan(msp.clone()), - msg, - code, - lvl, - true, - true); - } - - fn emit_struct(&mut self, db: &DiagnosticBuilder) { - let old_school = check_old_skool(); - let db_span = FullSpan(db.span.clone()); - self.emit_message(&FullSpan(db.span.clone()), - &db.message, - db.code.as_ref().map(|s| &**s), - db.level, - true, - true); - for child in &db.children { - let render_span = child.render_span - .clone() - .unwrap_or_else( - || FullSpan(child.span.clone())); - - if !old_school { - self.emit_message(&render_span, - &child.message, - None, - child.level, - false, - true); - } else { - let (render_span, show_snippet) = match render_span.span().primary_span() { - None => (db_span.clone(), false), - _ => (render_span, true) - }; - self.emit_message(&render_span, - &child.message, - None, - child.level, - false, - show_snippet); - } - } - } -} - -/// maximum number of lines we will print for each error; arbitrary. -pub const MAX_HIGHLIGHT_LINES: usize = 6; - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum ColorConfig { - Auto, - Always, - Never, -} - -impl ColorConfig { - fn use_color(&self) -> bool { - match *self { - ColorConfig::Always => true, - ColorConfig::Never => false, - ColorConfig::Auto => stderr_isatty(), - } - } -} - -/// A basic emitter for when we don't have access to a codemap or registry. Used -/// for reporting very early errors, etc. -pub struct BasicEmitter { - dst: Destination, -} - -impl CoreEmitter for BasicEmitter { - fn emit_message(&mut self, - _rsp: &RenderSpan, - msg: &str, - code: Option<&str>, - lvl: Level, - _is_header: bool, - _show_snippet: bool) { - // we ignore the span as we have no access to a codemap at this point - if let Err(e) = print_diagnostic(&mut self.dst, "", lvl, msg, code) { - panic!("failed to print diagnostics: {:?}", e); - } - } -} - -impl BasicEmitter { - pub fn stderr(color_config: ColorConfig) -> BasicEmitter { - if color_config.use_color() { - let dst = Destination::from_stderr(); - BasicEmitter { dst: dst } - } else { - BasicEmitter { dst: Raw(Box::new(io::stderr())) } - } - } -} - -pub struct EmitterWriter { - dst: Destination, - registry: Option<diagnostics::registry::Registry>, - cm: Rc<codemap::CodeMap>, - - /// Is this the first error emitted thus far? If not, we emit a - /// `\n` before the top-level errors. - first: bool, - - // For now, allow an old-school mode while we transition - old_school: bool, -} - -impl CoreEmitter for EmitterWriter { - fn emit_message(&mut self, - rsp: &RenderSpan, - msg: &str, - code: Option<&str>, - lvl: Level, - is_header: bool, - show_snippet: bool) { - match self.emit_message_(rsp, msg, code, lvl, is_header, show_snippet) { - Ok(()) => { } - Err(e) => panic!("failed to emit error: {}", e) - } - } -} - -/// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See -/// `EmitterWriter::print_maybe_styled` for details. -macro_rules! print_maybe_styled { - ($dst: expr, $style: expr, $($arg: tt)*) => { - $dst.print_maybe_styled(format_args!($($arg)*), $style, false) - } -} - -macro_rules! println_maybe_styled { - ($dst: expr, $style: expr, $($arg: tt)*) => { - $dst.print_maybe_styled(format_args!($($arg)*), $style, true) - } -} - -impl EmitterWriter { - pub fn stderr(color_config: ColorConfig, - registry: Option<diagnostics::registry::Registry>, - code_map: Rc<codemap::CodeMap>) - -> EmitterWriter { - let old_school = check_old_skool(); - if color_config.use_color() { - let dst = Destination::from_stderr(); - EmitterWriter { dst: dst, - registry: registry, - cm: code_map, - first: true, - old_school: old_school } - } else { - EmitterWriter { dst: Raw(Box::new(io::stderr())), - registry: registry, - cm: code_map, - first: true, - old_school: old_school } - } - } - - pub fn new(dst: Box<Write + Send>, - registry: Option<diagnostics::registry::Registry>, - code_map: Rc<codemap::CodeMap>) - -> EmitterWriter { - let old_school = check_old_skool(); - EmitterWriter { dst: Raw(dst), - registry: registry, - cm: code_map, - first: true, - old_school: old_school } - } - - fn emit_message_(&mut self, - rsp: &RenderSpan, - msg: &str, - code: Option<&str>, - lvl: Level, - is_header: bool, - show_snippet: bool) - -> io::Result<()> { - if is_header { - if self.first { - self.first = false; - } else { - if !self.old_school { - write!(self.dst, "\n")?; - } - } - } - - match code { - Some(code) if self.registry.as_ref() - .and_then(|registry| registry.find_description(code)) - .is_some() => { - let code_with_explain = String::from("--explain ") + code; - if self.old_school { - let loc = match rsp.span().primary_span() { - Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), - Some(ps) => self.cm.span_to_string(ps), - None => "".to_string() - }; - print_diagnostic(&mut self.dst, &loc, lvl, msg, Some(code))? - } - else { - print_diagnostic(&mut self.dst, "", lvl, msg, Some(&code_with_explain))? - } - } - _ => { - if self.old_school { - let loc = match rsp.span().primary_span() { - Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), - Some(ps) => self.cm.span_to_string(ps), - None => "".to_string() - }; - print_diagnostic(&mut self.dst, &loc, lvl, msg, code)? - } - else { - print_diagnostic(&mut self.dst, "", lvl, msg, code)? - } - } - } - - if !show_snippet { - return Ok(()); - } - - // Watch out for various nasty special spans; don't try to - // print any filename or anything for those. - match rsp.span().primary_span() { - Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => { - return Ok(()); - } - _ => { } - } - - // Otherwise, print out the snippet etc as needed. - match *rsp { - FullSpan(ref msp) => { - self.highlight_lines(msp, lvl)?; - if let Some(primary_span) = msp.primary_span() { - self.print_macro_backtrace(primary_span)?; - } - } - Suggestion(ref suggestion) => { - self.highlight_suggestion(suggestion)?; - if let Some(primary_span) = rsp.span().primary_span() { - self.print_macro_backtrace(primary_span)?; - } - } - } - if self.old_school { - match code { - Some(code) if self.registry.as_ref() - .and_then(|registry| registry.find_description(code)) - .is_some() => { - let loc = match rsp.span().primary_span() { - Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), - Some(ps) => self.cm.span_to_string(ps), - None => "".to_string() - }; - let msg = "run `rustc --explain ".to_string() + &code.to_string() + - "` to see a detailed explanation"; - print_diagnostic(&mut self.dst, &loc, Level::Help, &msg, - None)? - } - _ => () - } - } - Ok(()) - } - - fn highlight_suggestion(&mut self, suggestion: &CodeSuggestion) -> io::Result<()> - { - let primary_span = suggestion.msp.primary_span().unwrap(); - let lines = self.cm.span_to_lines(primary_span).unwrap(); - assert!(!lines.lines.is_empty()); - - let complete = suggestion.splice_lines(&self.cm); - let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES); - let display_lines = &lines.lines[..line_count]; - - let fm = &*lines.file; - // Calculate the widest number to format evenly - let max_digits = line_num_max_digits(display_lines.last().unwrap()); - - // print the suggestion without any line numbers, but leave - // space for them. This helps with lining up with previous - // snippets from the actual error being reported. - let mut lines = complete.lines(); - for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { - write!(&mut self.dst, "{0}:{1:2$} {3}\n", - fm.name, "", max_digits, line)?; - } - - // if we elided some lines, add an ellipsis - if let Some(_) = lines.next() { - write!(&mut self.dst, "{0:1$} {0:2$} ...\n", - "", fm.name.len(), max_digits)?; - } - - Ok(()) - } - - fn highlight_lines(&mut self, - msp: &MultiSpan, - lvl: Level) - -> io::Result<()> - { - let mut snippet_data = SnippetData::new(self.cm.clone(), - msp.primary_span()); - if self.old_school { - let mut output_vec = vec![]; - - for span_label in msp.span_labels() { - let mut snippet_data = SnippetData::new(self.cm.clone(), - Some(span_label.span)); - - snippet_data.push(span_label.span, - span_label.is_primary, - span_label.label); - if span_label.is_primary { - output_vec.insert(0, snippet_data); - } - else { - output_vec.push(snippet_data); - } - } - - for snippet_data in output_vec.iter() { - let rendered_lines = snippet_data.render_lines(); - for rendered_line in &rendered_lines { - for styled_string in &rendered_line.text { - self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?; - write!(&mut self.dst, "{}", styled_string.text)?; - self.dst.reset_attrs()?; - } - write!(&mut self.dst, "\n")?; - } - } - } - else { - for span_label in msp.span_labels() { - snippet_data.push(span_label.span, - span_label.is_primary, - span_label.label); - } - let rendered_lines = snippet_data.render_lines(); - for rendered_line in &rendered_lines { - for styled_string in &rendered_line.text { - self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?; - write!(&mut self.dst, "{}", styled_string.text)?; - self.dst.reset_attrs()?; - } - write!(&mut self.dst, "\n")?; - } - } - Ok(()) - } - - fn print_macro_backtrace(&mut self, - sp: Span) - -> io::Result<()> { - for trace in self.cm.macro_backtrace(sp) { - let mut diag_string = - format!("in this expansion of {}", trace.macro_decl_name); - if let Some(def_site_span) = trace.def_site_span { - diag_string.push_str( - &format!(" (defined in {})", - self.cm.span_to_filename(def_site_span))); - } - let snippet = self.cm.span_to_string(trace.call_site); - print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?; - } - Ok(()) - } -} - -fn line_num_max_digits(line: &codemap::LineInfo) -> usize { - let mut max_line_num = line.line_index + 1; - let mut digits = 0; - while max_line_num > 0 { - max_line_num /= 10; - digits += 1; - } - digits -} - -fn print_diagnostic(dst: &mut Destination, - topic: &str, - lvl: Level, - msg: &str, - code: Option<&str>) - -> io::Result<()> { - if !topic.is_empty() { - let old_school = check_old_skool(); - if !old_school { - write!(dst, "{}: ", topic)?; - } - else { - write!(dst, "{} ", topic)?; - } - dst.reset_attrs()?; - } - dst.start_attr(term::Attr::Bold)?; - dst.start_attr(term::Attr::ForegroundColor(lvl.color()))?; - write!(dst, "{}", lvl.to_string())?; - dst.reset_attrs()?; - write!(dst, ": ")?; - dst.start_attr(term::Attr::Bold)?; - write!(dst, "{}", msg)?; - - if let Some(code) = code { - let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA); - print_maybe_styled!(dst, style, " [{}]", code.clone())?; - } - - dst.reset_attrs()?; - write!(dst, "\n")?; - Ok(()) -} - -#[cfg(unix)] -fn stderr_isatty() -> bool { - use libc; - unsafe { libc::isatty(libc::STDERR_FILENO) != 0 } -} -#[cfg(windows)] -fn stderr_isatty() -> bool { - type DWORD = u32; - type BOOL = i32; - type HANDLE = *mut u8; - const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD; - extern "system" { - fn GetStdHandle(which: DWORD) -> HANDLE; - fn GetConsoleMode(hConsoleHandle: HANDLE, - lpMode: *mut DWORD) -> BOOL; - } - unsafe { - let handle = GetStdHandle(STD_ERROR_HANDLE); - let mut out = 0; - GetConsoleMode(handle, &mut out) != 0 - } -} - -enum Destination { - Terminal(Box<term::StderrTerminal>), - Raw(Box<Write + Send>), -} - -impl Destination { - fn from_stderr() -> Destination { - match term::stderr() { - Some(t) => Terminal(t), - None => Raw(Box::new(io::stderr())), - } - } - - fn apply_style(&mut self, - lvl: Level, - _kind: &RenderedLineKind, - style: Style) - -> io::Result<()> { - match style { - Style::FileNameStyle | - Style::LineAndColumn => { - } - Style::LineNumber => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?; - } - Style::Quotation => { - } - Style::OldSkoolNote => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_GREEN))?; - } - Style::OldSkoolNoteText => { - self.start_attr(term::Attr::Bold)?; - } - Style::UnderlinePrimary | Style::LabelPrimary => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(lvl.color()))?; - } - Style::UnderlineSecondary | Style::LabelSecondary => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?; - } - Style::NoStyle => { - } - } - Ok(()) - } - - fn start_attr(&mut self, attr: term::Attr) -> io::Result<()> { - match *self { - Terminal(ref mut t) => { t.attr(attr)?; } - Raw(_) => { } - } - Ok(()) - } - - fn reset_attrs(&mut self) -> io::Result<()> { - match *self { - Terminal(ref mut t) => { t.reset()?; } - Raw(_) => { } - } - Ok(()) - } - - fn print_maybe_styled(&mut self, - args: fmt::Arguments, - color: term::Attr, - print_newline_at_end: bool) - -> io::Result<()> { - match *self { - Terminal(ref mut t) => { - t.attr(color)?; - // If `msg` ends in a newline, we need to reset the color before - // the newline. We're making the assumption that we end up writing - // to a `LineBufferedWriter`, which means that emitting the reset - // after the newline ends up buffering the reset until we print - // another line or exit. Buffering the reset is a problem if we're - // sharing the terminal with any other programs (e.g. other rustc - // instances via `make -jN`). - // - // Note that if `msg` contains any internal newlines, this will - // result in the `LineBufferedWriter` flushing twice instead of - // once, which still leaves the opportunity for interleaved output - // to be miscolored. We assume this is rare enough that we don't - // have to worry about it. - t.write_fmt(args)?; - t.reset()?; - if print_newline_at_end { - t.write_all(b"\n") - } else { - Ok(()) - } - } - Raw(ref mut w) => { - w.write_fmt(args)?; - if print_newline_at_end { - w.write_all(b"\n") - } else { - Ok(()) - } - } - } - } -} - -impl Write for Destination { - fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { - match *self { - Terminal(ref mut t) => t.write(bytes), - Raw(ref mut w) => w.write(bytes), - } - } - fn flush(&mut self) -> io::Result<()> { - match *self { - Terminal(ref mut t) => t.flush(), - Raw(ref mut w) => w.flush(), - } - } -} - - -#[cfg(test)] -mod test { - use errors::{Level, CodeSuggestion}; - use super::EmitterWriter; - use codemap::{mk_sp, CodeMap, Span, MultiSpan, BytePos, NO_EXPANSION}; - use std::sync::{Arc, Mutex}; - use std::io::{self, Write}; - use std::str::from_utf8; - use std::rc::Rc; - - struct Sink(Arc<Mutex<Vec<u8>>>); - impl Write for Sink { - fn write(&mut self, data: &[u8]) -> io::Result<usize> { - Write::write(&mut *self.0.lock().unwrap(), data) - } - fn flush(&mut self) -> io::Result<()> { Ok(()) } - } - - /// Given a string like " ^~~~~~~~~~~~ ", produces a span - /// coverting that range. The idea is that the string has the same - /// length as the input, and we uncover the byte positions. Note - /// that this can span lines and so on. - fn span_from_selection(input: &str, selection: &str) -> Span { - assert_eq!(input.len(), selection.len()); - let left_index = selection.find('~').unwrap() as u32; - let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index); - Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION } - } - - // Diagnostic doesn't align properly in span where line number increases by one digit - #[test] - fn test_hilight_suggestion_issue_11715() { - let data = Arc::new(Mutex::new(Vec::new())); - let cm = Rc::new(CodeMap::new()); - let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone()); - let content = "abcdefg - koksi - line3 - line4 - cinq - line6 - line7 - line8 - line9 - line10 - e-lä-vän - tolv - dreizehn - "; - let file = cm.new_filemap_and_lines("dummy.txt", None, content); - let start = file.lines.borrow()[10]; - let end = file.lines.borrow()[11]; - let sp = mk_sp(start, end); - let lvl = Level::Error; - println!("highlight_lines"); - ew.highlight_lines(&sp.into(), lvl).unwrap(); - println!("done"); - let vec = data.lock().unwrap().clone(); - let vec: &[u8] = &vec; - let str = from_utf8(vec).unwrap(); - println!("r#\"\n{}\"#", str); - assert_eq!(str, &r#" - --> dummy.txt:11:1 - |> -11 |> e-lä-vän - |> ^ -"#[1..]); - } - - #[test] - fn test_single_span_splice() { - // Test that a `MultiSpan` containing a single span splices a substition correctly - let cm = CodeMap::new(); - let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; - let selection = " \n ~~\n~~~\n~~~~~ \n \n"; - cm.new_filemap_and_lines("blork.rs", None, inputtext); - let sp = span_from_selection(inputtext, selection); - let msp: MultiSpan = sp.into(); - - // check that we are extracting the text we thought we were extracting - assert_eq!(&cm.span_to_snippet(sp).unwrap(), "BB\nCCC\nDDDDD"); - - let substitute = "ZZZZZZ".to_owned(); - let expected = "bbbbZZZZZZddddd"; - let suggest = CodeSuggestion { - msp: msp, - substitutes: vec![substitute], - }; - assert_eq!(suggest.splice_lines(&cm), expected); - } - - #[test] - fn test_multi_span_splice() { - // Test that a `MultiSpan` containing multiple spans splices a substition correctly - let cm = CodeMap::new(); - let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; - let selection1 = " \n \n \n \n ~ \n"; // intentionally out of order - let selection2 = " \n ~~\n~~~\n~~~~~ \n \n"; - cm.new_filemap_and_lines("blork.rs", None, inputtext); - let sp1 = span_from_selection(inputtext, selection1); - let sp2 = span_from_selection(inputtext, selection2); - let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]); - - let expected = "bbbbZZZZZZddddd\neXYZe"; - let suggest = CodeSuggestion { - msp: msp, - substitutes: vec!["ZZZZZZ".to_owned(), - "XYZ".to_owned()] - }; - - assert_eq!(suggest.splice_lines(&cm), expected); - } - - #[test] - fn test_multispan_highlight() { - let data = Arc::new(Mutex::new(Vec::new())); - let cm = Rc::new(CodeMap::new()); - let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone()); - - let inp = "_____aaaaaa____bbbbbb__cccccdd_"; - let sp1 = " ~~~~~~ "; - let sp2 = " ~~~~~~ "; - let sp3 = " ~~~~~ "; - let sp4 = " ~~~~ "; - let sp34 = " ~~~~~~~ "; - - let expect_start = &r#" - --> dummy.txt:1:6 - |> -1 |> _____aaaaaa____bbbbbb__cccccdd_ - |> ^^^^^^ ^^^^^^ ^^^^^^^ -"#[1..]; - - let span = |sp, expected| { - let sp = span_from_selection(inp, sp); - assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected); - sp - }; - cm.new_filemap_and_lines("dummy.txt", None, inp); - let sp1 = span(sp1, "aaaaaa"); - let sp2 = span(sp2, "bbbbbb"); - let sp3 = span(sp3, "ccccc"); - let sp4 = span(sp4, "ccdd"); - let sp34 = span(sp34, "cccccdd"); - - let spans = vec![sp1, sp2, sp3, sp4]; - - let test = |expected, highlight: &mut FnMut()| { - data.lock().unwrap().clear(); - highlight(); - let vec = data.lock().unwrap().clone(); - let actual = from_utf8(&vec[..]).unwrap(); - println!("actual=\n{}", actual); - assert_eq!(actual, expected); - }; - - let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]); - test(expect_start, &mut || { - diag.highlight_lines(&msp, Level::Error).unwrap(); - }); - test(expect_start, &mut || { - let msp = MultiSpan::from_spans(spans.clone()); - diag.highlight_lines(&msp, Level::Error).unwrap(); - }); - } - - #[test] - fn test_huge_multispan_highlight() { - let data = Arc::new(Mutex::new(Vec::new())); - let cm = Rc::new(CodeMap::new()); - let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone()); - - let inp = "aaaaa\n\ - aaaaa\n\ - aaaaa\n\ - bbbbb\n\ - ccccc\n\ - xxxxx\n\ - yyyyy\n\ - _____\n\ - ddd__eee_\n\ - elided\n\ - __f_gg"; - let file = cm.new_filemap_and_lines("dummy.txt", None, inp); - - let span = |lo, hi, (off_lo, off_hi)| { - let lines = file.lines.borrow(); - let (mut lo, mut hi): (BytePos, BytePos) = (lines[lo], lines[hi]); - lo.0 += off_lo; - hi.0 += off_hi; - mk_sp(lo, hi) - }; - let sp0 = span(4, 6, (0, 5)); - let sp1 = span(0, 6, (0, 5)); - let sp2 = span(8, 8, (0, 3)); - let sp3 = span(8, 8, (5, 8)); - let sp4 = span(10, 10, (2, 3)); - let sp5 = span(10, 10, (4, 6)); - - let expect0 = &r#" - --> dummy.txt:5:1 - |> -5 |> ccccc - |> ^ -... -9 |> ddd__eee_ - |> ^^^ ^^^ -10 |> elided -11 |> __f_gg - |> ^ ^^ -"#[1..]; - - let expect = &r#" - --> dummy.txt:1:1 - |> -1 |> aaaaa - |> ^ -... -9 |> ddd__eee_ - |> ^^^ ^^^ -10 |> elided -11 |> __f_gg - |> ^ ^^ -"#[1..]; - - macro_rules! test { - ($expected: expr, $highlight: expr) => ({ - data.lock().unwrap().clear(); - $highlight(); - let vec = data.lock().unwrap().clone(); - let actual = from_utf8(&vec[..]).unwrap(); - println!("actual:"); - println!("{}", actual); - println!("expected:"); - println!("{}", $expected); - assert_eq!(&actual[..], &$expected[..]); - }); - } - - let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]); - let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]); - - test!(expect0, || { - diag.highlight_lines(&msp0, Level::Error).unwrap(); - }); - test!(expect, || { - diag.highlight_lines(&msp, Level::Error).unwrap(); - }); - } -} diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs deleted file mode 100644 index f06672fe111..00000000000 --- a/src/libsyntax/errors/mod.rs +++ /dev/null @@ -1,711 +0,0 @@ -// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub use errors::emitter::ColorConfig; - -use self::Level::*; -use self::RenderSpan::*; - -use codemap::{self, CodeMap, MultiSpan, NO_EXPANSION, Span}; -use diagnostics; -use errors::emitter::{Emitter, EmitterWriter}; - -use std::cell::{RefCell, Cell}; -use std::{error, fmt}; -use std::rc::Rc; -use std::thread::panicking; -use term; - -pub mod emitter; -pub mod json; -pub mod snippet; - -#[derive(Clone)] -pub enum RenderSpan { - /// A FullSpan renders with both with an initial line for the - /// message, prefixed by file:linenum, followed by a summary of - /// the source code covered by the span. - FullSpan(MultiSpan), - - /// A suggestion renders with both with an initial line for the - /// message, prefixed by file:linenum, followed by a summary - /// of hypothetical source code, where each `String` is spliced - /// into the lines in place of the code covered by each span. - Suggestion(CodeSuggestion), -} - -#[derive(Clone)] -pub struct CodeSuggestion { - msp: MultiSpan, - substitutes: Vec<String>, -} - -impl RenderSpan { - fn span(&self) -> &MultiSpan { - match *self { - FullSpan(ref msp) | - Suggestion(CodeSuggestion { ref msp, .. }) => - msp - } - } -} - -impl CodeSuggestion { - /// Returns the assembled code suggestion. - pub fn splice_lines(&self, cm: &CodeMap) -> String { - use codemap::{CharPos, Loc, Pos}; - - fn push_trailing(buf: &mut String, line_opt: Option<&str>, - lo: &Loc, hi_opt: Option<&Loc>) { - let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi|hi.col.to_usize())); - if let Some(line) = line_opt { - if line.len() > lo { - buf.push_str(match hi_opt { - Some(hi) => &line[lo..hi], - None => &line[lo..], - }); - } - if let None = hi_opt { - buf.push('\n'); - } - } - } - - let mut primary_spans = self.msp.primary_spans().to_owned(); - - assert_eq!(primary_spans.len(), self.substitutes.len()); - if primary_spans.is_empty() { - return format!(""); - } - - // Assumption: all spans are in the same file, and all spans - // are disjoint. Sort in ascending order. - primary_spans.sort_by_key(|sp| sp.lo); - - // Find the bounding span. - let lo = primary_spans.iter().map(|sp| sp.lo).min().unwrap(); - let hi = primary_spans.iter().map(|sp| sp.hi).min().unwrap(); - let bounding_span = Span { lo: lo, hi: hi, expn_id: NO_EXPANSION }; - let lines = cm.span_to_lines(bounding_span).unwrap(); - assert!(!lines.lines.is_empty()); - - // To build up the result, we do this for each span: - // - push the line segment trailing the previous span - // (at the beginning a "phantom" span pointing at the start of the line) - // - push lines between the previous and current span (if any) - // - if the previous and current span are not on the same line - // push the line segment leading up to the current span - // - splice in the span substitution - // - // Finally push the trailing line segment of the last span - let fm = &lines.file; - let mut prev_hi = cm.lookup_char_pos(bounding_span.lo); - prev_hi.col = CharPos::from_usize(0); - - let mut prev_line = fm.get_line(lines.lines[0].line_index); - let mut buf = String::new(); - - for (sp, substitute) in primary_spans.iter().zip(self.substitutes.iter()) { - let cur_lo = cm.lookup_char_pos(sp.lo); - if prev_hi.line == cur_lo.line { - push_trailing(&mut buf, prev_line, &prev_hi, Some(&cur_lo)); - } else { - push_trailing(&mut buf, prev_line, &prev_hi, None); - // push lines between the previous and current span (if any) - for idx in prev_hi.line..(cur_lo.line - 1) { - if let Some(line) = fm.get_line(idx) { - buf.push_str(line); - buf.push('\n'); - } - } - if let Some(cur_line) = fm.get_line(cur_lo.line - 1) { - buf.push_str(&cur_line[.. cur_lo.col.to_usize()]); - } - } - buf.push_str(substitute); - prev_hi = cm.lookup_char_pos(sp.hi); - prev_line = fm.get_line(prev_hi.line - 1); - } - push_trailing(&mut buf, prev_line, &prev_hi, None); - // remove trailing newline - buf.pop(); - buf - } -} - -/// Used as a return value to signify a fatal error occurred. (It is also -/// used as the argument to panic at the moment, but that will eventually -/// not be true.) -#[derive(Copy, Clone, Debug)] -#[must_use] -pub struct FatalError; - -impl fmt::Display for FatalError { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "parser fatal error") - } -} - -impl error::Error for FatalError { - fn description(&self) -> &str { - "The parser has encountered a fatal error" - } -} - -/// Signifies that the compiler died with an explicit call to `.bug` -/// or `.span_bug` rather than a failed assertion, etc. -#[derive(Copy, Clone, Debug)] -pub struct ExplicitBug; - -impl fmt::Display for ExplicitBug { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "parser internal bug") - } -} - -impl error::Error for ExplicitBug { - fn description(&self) -> &str { - "The parser has encountered an internal bug" - } -} - -/// Used for emitting structured error messages and other diagnostic information. -#[must_use] -#[derive(Clone)] -pub struct DiagnosticBuilder<'a> { - handler: &'a Handler, - level: Level, - message: String, - code: Option<String>, - span: MultiSpan, - children: Vec<SubDiagnostic>, -} - -/// For example a note attached to an error. -#[derive(Clone)] -struct SubDiagnostic { - level: Level, - message: String, - span: MultiSpan, - render_span: Option<RenderSpan>, -} - -impl<'a> DiagnosticBuilder<'a> { - /// Emit the diagnostic. - pub fn emit(&mut self) { - if self.cancelled() { - return; - } - - self.handler.emit.borrow_mut().emit_struct(&self); - self.cancel(); - self.handler.panic_if_treat_err_as_bug(); - - // if self.is_fatal() { - // panic!(FatalError); - // } - } - - /// Cancel the diagnostic (a structured diagnostic must either be emitted or - /// cancelled or it will panic when dropped). - /// BEWARE: if this DiagnosticBuilder is an error, then creating it will - /// bump the error count on the Handler and cancelling it won't undo that. - /// If you want to decrement the error count you should use `Handler::cancel`. - pub fn cancel(&mut self) { - self.level = Level::Cancelled; - } - - pub fn cancelled(&self) -> bool { - self.level == Level::Cancelled - } - - pub fn is_fatal(&self) -> bool { - self.level == Level::Fatal - } - - /// Add a span/label to be included in the resulting snippet. - /// This is pushed onto the `MultiSpan` that was created when the - /// diagnostic was first built. If you don't call this function at - /// all, and you just supplied a `Span` to create the diagnostic, - /// then the snippet will just include that `Span`, which is - /// called the primary span. - pub fn span_label(&mut self, span: Span, label: &fmt::Display) - -> &mut DiagnosticBuilder<'a> { - self.span.push_span_label(span, format!("{}", label)); - self - } - - pub fn note_expected_found(&mut self, - label: &fmt::Display, - expected: &fmt::Display, - found: &fmt::Display) - -> &mut DiagnosticBuilder<'a> - { - // For now, just attach these as notes - self.note(&format!("expected {} `{}`", label, expected)); - self.note(&format!(" found {} `{}`", label, found)); - self - } - - pub fn note(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, MultiSpan::new(), None); - self - } - pub fn span_note<S: Into<MultiSpan>>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, sp.into(), None); - self - } - pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Warning, msg, MultiSpan::new(), None); - self - } - pub fn span_warn<S: Into<MultiSpan>>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Warning, msg, sp.into(), None); - self - } - pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, MultiSpan::new(), None); - self - } - pub fn span_help<S: Into<MultiSpan>>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, sp.into(), None); - self - } - /// Prints out a message with a suggested edit of the code. - /// - /// See `diagnostic::RenderSpan::Suggestion` for more information. - pub fn span_suggestion<S: Into<MultiSpan>>(&mut self, - sp: S, - msg: &str, - suggestion: String) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, MultiSpan::new(), Some(Suggestion(CodeSuggestion { - msp: sp.into(), - substitutes: vec![suggestion], - }))); - self - } - - pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self { - self.span = sp.into(); - self - } - - pub fn code(&mut self, s: String) -> &mut Self { - self.code = Some(s); - self - } - - pub fn message(&self) -> &str { - &self.message - } - - pub fn level(&self) -> Level { - self.level - } - - /// Convenience function for internal use, clients should use one of the - /// struct_* methods on Handler. - fn new(handler: &'a Handler, - level: Level, - message: &str) -> DiagnosticBuilder<'a> { - DiagnosticBuilder { - handler: handler, - level: level, - message: message.to_owned(), - code: None, - span: MultiSpan::new(), - children: vec![], - } - } - - /// Convenience function for internal use, clients should use one of the - /// public methods above. - fn sub(&mut self, - level: Level, - message: &str, - span: MultiSpan, - render_span: Option<RenderSpan>) { - let sub = SubDiagnostic { - level: level, - message: message.to_owned(), - span: span, - render_span: render_span, - }; - self.children.push(sub); - } -} - -impl<'a> fmt::Debug for DiagnosticBuilder<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.message.fmt(f) - } -} - -/// Destructor bomb - a DiagnosticBuilder must be either emitted or cancelled or -/// we emit a bug. -impl<'a> Drop for DiagnosticBuilder<'a> { - fn drop(&mut self) { - if !panicking() && !self.cancelled() { - self.handler.emit.borrow_mut().emit(&MultiSpan::new(), - "Error constructed but not emitted", - None, - Bug); - panic!(); - } - } -} - -/// A handler deals with errors; certain errors -/// (fatal, bug, unimpl) may cause immediate exit, -/// others log errors for later reporting. -pub struct Handler { - err_count: Cell<usize>, - emit: RefCell<Box<Emitter>>, - pub can_emit_warnings: bool, - treat_err_as_bug: bool, - continue_after_error: Cell<bool>, - delayed_span_bug: RefCell<Option<(MultiSpan, String)>>, -} - -impl Handler { - pub fn with_tty_emitter(color_config: ColorConfig, - registry: Option<diagnostics::registry::Registry>, - can_emit_warnings: bool, - treat_err_as_bug: bool, - cm: Rc<codemap::CodeMap>) - -> Handler { - let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm)); - Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter) - } - - pub fn with_emitter(can_emit_warnings: bool, - treat_err_as_bug: bool, - e: Box<Emitter>) -> Handler { - Handler { - err_count: Cell::new(0), - emit: RefCell::new(e), - can_emit_warnings: can_emit_warnings, - treat_err_as_bug: treat_err_as_bug, - continue_after_error: Cell::new(true), - delayed_span_bug: RefCell::new(None), - } - } - - pub fn set_continue_after_error(&self, continue_after_error: bool) { - self.continue_after_error.set(continue_after_error); - } - - pub fn struct_dummy<'a>(&'a self) -> DiagnosticBuilder<'a> { - DiagnosticBuilder::new(self, Level::Cancelled, "") - } - - pub fn struct_span_warn<'a, S: Into<MultiSpan>>(&'a self, - sp: S, - msg: &str) - -> DiagnosticBuilder<'a> { - let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); - result.set_span(sp); - if !self.can_emit_warnings { - result.cancel(); - } - result - } - pub fn struct_span_warn_with_code<'a, S: Into<MultiSpan>>(&'a self, - sp: S, - msg: &str, - code: &str) - -> DiagnosticBuilder<'a> { - let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); - result.set_span(sp); - result.code(code.to_owned()); - if !self.can_emit_warnings { - result.cancel(); - } - result - } - pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { - let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); - if !self.can_emit_warnings { - result.cancel(); - } - result - } - pub fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self, - sp: S, - msg: &str) - -> DiagnosticBuilder<'a> { - self.bump_err_count(); - let mut result = DiagnosticBuilder::new(self, Level::Error, msg); - result.set_span(sp); - result - } - pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self, - sp: S, - msg: &str, - code: &str) - -> DiagnosticBuilder<'a> { - self.bump_err_count(); - let mut result = DiagnosticBuilder::new(self, Level::Error, msg); - result.set_span(sp); - result.code(code.to_owned()); - result - } - pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { - self.bump_err_count(); - DiagnosticBuilder::new(self, Level::Error, msg) - } - pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(&'a self, - sp: S, - msg: &str) - -> DiagnosticBuilder<'a> { - self.bump_err_count(); - let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg); - result.set_span(sp); - result - } - pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(&'a self, - sp: S, - msg: &str, - code: &str) - -> DiagnosticBuilder<'a> { - self.bump_err_count(); - let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg); - result.set_span(sp); - result.code(code.to_owned()); - result - } - pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { - self.bump_err_count(); - DiagnosticBuilder::new(self, Level::Fatal, msg) - } - - pub fn cancel(&mut self, err: &mut DiagnosticBuilder) { - if err.level == Level::Error || err.level == Level::Fatal { - assert!(self.has_errors()); - self.err_count.set(self.err_count.get() + 1); - } - err.cancel(); - } - - fn panic_if_treat_err_as_bug(&self) { - if self.treat_err_as_bug { - panic!("encountered error with `-Z treat_err_as_bug"); - } - } - - pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) - -> FatalError { - self.emit(&sp.into(), msg, Fatal); - self.bump_err_count(); - self.panic_if_treat_err_as_bug(); - return FatalError; - } - pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) - -> FatalError { - self.emit_with_code(&sp.into(), msg, code, Fatal); - self.bump_err_count(); - self.panic_if_treat_err_as_bug(); - return FatalError; - } - pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { - self.emit(&sp.into(), msg, Error); - self.bump_err_count(); - self.panic_if_treat_err_as_bug(); - } - pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) { - self.emit_with_code(&sp.into(), msg, code, Error); - self.bump_err_count(); - self.panic_if_treat_err_as_bug(); - } - pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { - self.emit(&sp.into(), msg, Warning); - } - pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) { - self.emit_with_code(&sp.into(), msg, code, Warning); - } - pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! { - self.emit(&sp.into(), msg, Bug); - panic!(ExplicitBug); - } - pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { - let mut delayed = self.delayed_span_bug.borrow_mut(); - *delayed = Some((sp.into(), msg.to_string())); - } - pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { - self.emit(&sp.into(), msg, Bug); - self.bump_err_count(); - } - pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { - self.emit.borrow_mut().emit(&sp.into(), msg, None, Note); - } - pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! { - self.span_bug(sp, &format!("unimplemented {}", msg)); - } - pub fn fatal(&self, msg: &str) -> FatalError { - if self.treat_err_as_bug { - self.bug(msg); - } - self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Fatal); - self.bump_err_count(); - FatalError - } - pub fn err(&self, msg: &str) { - if self.treat_err_as_bug { - self.bug(msg); - } - self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Error); - self.bump_err_count(); - } - pub fn warn(&self, msg: &str) { - self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Warning); - } - pub fn note_without_error(&self, msg: &str) { - self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Note); - } - pub fn bug(&self, msg: &str) -> ! { - self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Bug); - panic!(ExplicitBug); - } - pub fn unimpl(&self, msg: &str) -> ! { - self.bug(&format!("unimplemented {}", msg)); - } - - pub fn bump_err_count(&self) { - self.err_count.set(self.err_count.get() + 1); - } - - pub fn err_count(&self) -> usize { - self.err_count.get() - } - - pub fn has_errors(&self) -> bool { - self.err_count.get() > 0 - } - pub fn abort_if_errors(&self) { - let s; - match self.err_count.get() { - 0 => { - let delayed_bug = self.delayed_span_bug.borrow(); - match *delayed_bug { - Some((ref span, ref errmsg)) => { - self.span_bug(span.clone(), errmsg); - }, - _ => {} - } - - return; - } - 1 => s = "aborting due to previous error".to_string(), - _ => { - s = format!("aborting due to {} previous errors", - self.err_count.get()); - } - } - - panic!(self.fatal(&s)); - } - pub fn emit(&self, - msp: &MultiSpan, - msg: &str, - lvl: Level) { - if lvl == Warning && !self.can_emit_warnings { return } - self.emit.borrow_mut().emit(&msp, msg, None, lvl); - if !self.continue_after_error.get() { self.abort_if_errors(); } - } - pub fn emit_with_code(&self, - msp: &MultiSpan, - msg: &str, - code: &str, - lvl: Level) { - if lvl == Warning && !self.can_emit_warnings { return } - self.emit.borrow_mut().emit(&msp, msg, Some(code), lvl); - if !self.continue_after_error.get() { self.abort_if_errors(); } - } -} - - -#[derive(Copy, PartialEq, Clone, Debug)] -pub enum Level { - Bug, - Fatal, - // An error which while not immediately fatal, should stop the compiler - // progressing beyond the current phase. - PhaseFatal, - Error, - Warning, - Note, - Help, - Cancelled, -} - -impl fmt::Display for Level { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.to_str().fmt(f) - } -} - -impl Level { - fn color(self) -> term::color::Color { - match self { - Bug | Fatal | PhaseFatal | Error => term::color::BRIGHT_RED, - Warning => term::color::YELLOW, - Note => term::color::BRIGHT_GREEN, - Help => term::color::BRIGHT_CYAN, - Cancelled => unreachable!(), - } - } - - fn to_str(self) -> &'static str { - match self { - Bug => "error: internal compiler error", - Fatal | PhaseFatal | Error => "error", - Warning => "warning", - Note => "note", - Help => "help", - Cancelled => panic!("Shouldn't call on cancelled error"), - } - } -} - -pub fn expect<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T where - M: FnOnce() -> String, -{ - match opt { - Some(t) => t, - None => diag.bug(&msg()), - } -} - -/// True if we should use the old-skool error format style. This is -/// the default setting until the new errors are deemed stable enough -/// for general use. -/// -/// FIXME(#33240) -#[cfg(not(test))] -pub fn check_old_skool() -> bool { - use std::env; - env::var("RUST_NEW_ERROR_FORMAT").is_err() -} - -/// For unit tests, use the new format. -#[cfg(test)] -pub fn check_old_skool() -> bool { - false -} diff --git a/src/libsyntax/errors/snippet/mod.rs b/src/libsyntax/errors/snippet/mod.rs deleted file mode 100644 index 2a43a14ddf8..00000000000 --- a/src/libsyntax/errors/snippet/mod.rs +++ /dev/null @@ -1,888 +0,0 @@ -// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Code for annotating snippets. - -use codemap::{CharPos, CodeMap, FileMap, LineInfo, Span}; -use errors::check_old_skool; -use std::cmp; -use std::rc::Rc; -use std::mem; - -mod test; - -#[derive(Clone)] -pub struct SnippetData { - codemap: Rc<CodeMap>, - files: Vec<FileInfo>, -} - -#[derive(Clone)] -pub struct FileInfo { - file: Rc<FileMap>, - - /// The "primary file", if any, gets a `-->` marker instead of - /// `>>>`, and has a line-number/column printed and not just a - /// filename. It appears first in the listing. It is known to - /// contain at least one primary span, though primary spans (which - /// are designated with `^^^`) may also occur in other files. - primary_span: Option<Span>, - - lines: Vec<Line>, -} - -#[derive(Clone, Debug)] -struct Line { - line_index: usize, - annotations: Vec<Annotation>, -} - -#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] -struct Annotation { - /// Start column, 0-based indexing -- counting *characters*, not - /// utf-8 bytes. Note that it is important that this field goes - /// first, so that when we sort, we sort orderings by start - /// column. - start_col: usize, - - /// End column within the line (exclusive) - end_col: usize, - - /// Is this annotation derived from primary span - is_primary: bool, - - /// Is this a large span minimized down to a smaller span - is_minimized: bool, - - /// Optional label to display adjacent to the annotation. - label: Option<String>, -} - -#[derive(Debug)] -pub struct RenderedLine { - pub text: Vec<StyledString>, - pub kind: RenderedLineKind, -} - -#[derive(Debug)] -pub struct StyledString { - pub text: String, - pub style: Style, -} - -#[derive(Debug)] -pub struct StyledBuffer { - text: Vec<Vec<char>>, - styles: Vec<Vec<Style>> -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Style { - FileNameStyle, - LineAndColumn, - LineNumber, - Quotation, - UnderlinePrimary, - UnderlineSecondary, - LabelPrimary, - LabelSecondary, - OldSkoolNoteText, - OldSkoolNote, - NoStyle, -} - -#[derive(Debug, Clone)] -pub enum RenderedLineKind { - PrimaryFileName, - OtherFileName, - SourceText { - file: Rc<FileMap>, - line_index: usize, - }, - Annotations, - Elision, -} - -impl SnippetData { - pub fn new(codemap: Rc<CodeMap>, - primary_span: Option<Span>) // (*) - -> Self { - // (*) The primary span indicates the file that must appear - // first, and which will have a line number etc in its - // name. Outside of tests, this is always `Some`, but for many - // tests it's not relevant to test this portion of the logic, - // and it's tedious to pick a primary span (read: tedious to - // port older tests that predate the existence of a primary - // span). - - debug!("SnippetData::new(primary_span={:?})", primary_span); - - let mut data = SnippetData { - codemap: codemap.clone(), - files: vec![] - }; - if let Some(primary_span) = primary_span { - let lo = codemap.lookup_char_pos(primary_span.lo); - data.files.push( - FileInfo { - file: lo.file, - primary_span: Some(primary_span), - lines: vec![], - }); - } - data - } - - pub fn push(&mut self, span: Span, is_primary: bool, label: Option<String>) { - debug!("SnippetData::push(span={:?}, is_primary={}, label={:?})", - span, is_primary, label); - - let file_lines = match self.codemap.span_to_lines(span) { - Ok(file_lines) => file_lines, - Err(_) => { - // ignore unprintable spans completely. - return; - } - }; - - self.file(&file_lines.file) - .push_lines(&file_lines.lines, is_primary, label); - } - - fn file(&mut self, file_map: &Rc<FileMap>) -> &mut FileInfo { - let index = self.files.iter().position(|f| f.file.name == file_map.name); - if let Some(index) = index { - return &mut self.files[index]; - } - - self.files.push( - FileInfo { - file: file_map.clone(), - lines: vec![], - primary_span: None, - }); - self.files.last_mut().unwrap() - } - - pub fn render_lines(&self) -> Vec<RenderedLine> { - debug!("SnippetData::render_lines()"); - - let mut rendered_lines: Vec<_> = - self.files.iter() - .flat_map(|f| f.render_file_lines(&self.codemap)) - .collect(); - prepend_prefixes(&mut rendered_lines); - trim_lines(&mut rendered_lines); - rendered_lines - } -} - -pub trait StringSource { - fn make_string(self) -> String; -} - -impl StringSource for String { - fn make_string(self) -> String { - self - } -} - -impl StringSource for Vec<char> { - fn make_string(self) -> String { - self.into_iter().collect() - } -} - -impl<S> From<(S, Style, RenderedLineKind)> for RenderedLine - where S: StringSource -{ - fn from((text, style, kind): (S, Style, RenderedLineKind)) -> Self { - RenderedLine { - text: vec![StyledString { - text: text.make_string(), - style: style, - }], - kind: kind, - } - } -} - -impl<S1,S2> From<(S1, Style, S2, Style, RenderedLineKind)> for RenderedLine - where S1: StringSource, S2: StringSource -{ - fn from(tuple: (S1, Style, S2, Style, RenderedLineKind)) -> Self { - let (text1, style1, text2, style2, kind) = tuple; - RenderedLine { - text: vec![ - StyledString { - text: text1.make_string(), - style: style1, - }, - StyledString { - text: text2.make_string(), - style: style2, - } - ], - kind: kind, - } - } -} - -impl RenderedLine { - fn trim_last(&mut self) { - if let Some(last_text) = self.text.last_mut() { - let len = last_text.text.trim_right().len(); - last_text.text.truncate(len); - } - } -} - -impl RenderedLineKind { - fn prefix(&self) -> StyledString { - match *self { - RenderedLineKind::SourceText { file: _, line_index } => - StyledString { - text: format!("{}", line_index + 1), - style: Style::LineNumber, - }, - RenderedLineKind::Elision => - StyledString { - text: String::from("..."), - style: Style::LineNumber, - }, - RenderedLineKind::PrimaryFileName | - RenderedLineKind::OtherFileName | - RenderedLineKind::Annotations => - StyledString { - text: String::from(""), - style: Style::LineNumber, - }, - } - } -} - -impl StyledBuffer { - fn new() -> StyledBuffer { - StyledBuffer { text: vec![], styles: vec![] } - } - - fn render(&self, source_kind: RenderedLineKind) -> Vec<RenderedLine> { - let mut output: Vec<RenderedLine> = vec![]; - let mut styled_vec: Vec<StyledString> = vec![]; - - for (row, row_style) in self.text.iter().zip(&self.styles) { - let mut current_style = Style::NoStyle; - let mut current_text = String::new(); - - for (&c, &s) in row.iter().zip(row_style) { - if s != current_style { - if !current_text.is_empty() { - styled_vec.push(StyledString { text: current_text, style: current_style }); - } - current_style = s; - current_text = String::new(); - } - current_text.push(c); - } - if !current_text.is_empty() { - styled_vec.push(StyledString { text: current_text, style: current_style }); - } - - if output.is_empty() { - //We know our first output line is source and the rest are highlights and labels - output.push(RenderedLine { text: styled_vec, kind: source_kind.clone() }); - } else { - output.push(RenderedLine { text: styled_vec, kind: RenderedLineKind::Annotations }); - } - styled_vec = vec![]; - } - - output - } - - fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) { - while line >= self.text.len() { - self.text.push(vec![]); - self.styles.push(vec![]); - } - - if col < self.text[line].len() { - self.text[line][col] = chr; - self.styles[line][col] = style; - } else { - let mut i = self.text[line].len(); - while i < col { - let s = match self.text[0].get(i) { - Some(&'\t') => '\t', - _ => ' ' - }; - self.text[line].push(s); - self.styles[line].push(Style::NoStyle); - i += 1; - } - self.text[line].push(chr); - self.styles[line].push(style); - } - } - - fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) { - let mut n = col; - for c in string.chars() { - self.putc(line, n, c, style); - n += 1; - } - } - - fn set_style(&mut self, line: usize, col: usize, style: Style) { - if self.styles.len() > line && self.styles[line].len() > col { - self.styles[line][col] = style; - } - } - - fn append(&mut self, line: usize, string: &str, style: Style) { - if line >= self.text.len() { - self.puts(line, 0, string, style); - } else { - let col = self.text[line].len(); - self.puts(line, col, string, style); - } - } -} - -impl FileInfo { - fn push_lines(&mut self, - lines: &[LineInfo], - is_primary: bool, - label: Option<String>) { - assert!(lines.len() > 0); - - // If a span covers multiple lines, we reduce it to a single - // point at the start of the span. This means that instead - // of producing output like this: - // - // ``` - // --> foo.rs:2:1 - // 2 |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>) - // |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // 3 |> -> Set<LR0Item<'grammar>> - // |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // (and so on) - // ``` - // - // we produce: - // - // ``` - // --> foo.rs:2:1 - // 2 |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>) - // ^ - // ``` - // - // Basically, although this loses information, multi-line spans just - // never look good. - - let (line, start_col, mut end_col, is_minimized) = if lines.len() == 1 { - (lines[0].line_index, lines[0].start_col, lines[0].end_col, false) - } else { - (lines[0].line_index, lines[0].start_col, CharPos(lines[0].start_col.0 + 1), true) - }; - - // Watch out for "empty spans". If we get a span like 6..6, we - // want to just display a `^` at 6, so convert that to - // 6..7. This is degenerate input, but it's best to degrade - // gracefully -- and the parser likes to suply a span like - // that for EOF, in particular. - if start_col == end_col { - end_col.0 += 1; - } - - let index = self.ensure_source_line(line); - self.lines[index].push_annotation(start_col, - end_col, - is_primary, - is_minimized, - label); - } - - /// Ensure that we have a `Line` struct corresponding to - /// `line_index` in the file. If we already have some other lines, - /// then this will add the intervening lines to ensure that we - /// have a complete snippet. (Note that when we finally display, - /// some of those lines may be elided.) - fn ensure_source_line(&mut self, line_index: usize) -> usize { - if self.lines.is_empty() { - self.lines.push(Line::new(line_index)); - return 0; - } - - // Find the range of lines we have thus far. - let first_line_index = self.lines.first().unwrap().line_index; - let last_line_index = self.lines.last().unwrap().line_index; - assert!(first_line_index <= last_line_index); - - // If the new line is lower than all the lines we have thus - // far, then insert the new line and any intervening lines at - // the front. In a silly attempt at micro-optimization, we - // don't just call `insert` repeatedly, but instead make a new - // (empty) vector, pushing the new lines onto it, and then - // appending the old vector. - if line_index < first_line_index { - let lines = mem::replace(&mut self.lines, vec![]); - self.lines.extend( - (line_index .. first_line_index) - .map(|line| Line::new(line)) - .chain(lines)); - return 0; - } - - // If the new line comes after the ones we have so far, insert - // lines for it. - if line_index > last_line_index { - self.lines.extend( - (last_line_index+1 .. line_index+1) - .map(|line| Line::new(line))); - return self.lines.len() - 1; - } - - // Otherwise it should already exist. - return line_index - first_line_index; - } - - fn render_file_lines(&self, codemap: &Rc<CodeMap>) -> Vec<RenderedLine> { - let old_school = check_old_skool(); - - // As a first step, we elide any instance of more than one - // continuous unannotated line. - - let mut lines_iter = self.lines.iter(); - let mut output = vec![]; - - // First insert the name of the file. - if !old_school { - match self.primary_span { - Some(span) => { - let lo = codemap.lookup_char_pos(span.lo); - output.push(RenderedLine { - text: vec![StyledString { - text: lo.file.name.clone(), - style: Style::FileNameStyle, - }, StyledString { - text: format!(":{}:{}", lo.line, lo.col.0 + 1), - style: Style::LineAndColumn, - }], - kind: RenderedLineKind::PrimaryFileName, - }); - output.push(RenderedLine { - text: vec![StyledString { - text: "".to_string(), - style: Style::FileNameStyle, - }], - kind: RenderedLineKind::Annotations, - }); - } - None => { - output.push(RenderedLine { - text: vec![StyledString { - text: self.file.name.clone(), - style: Style::FileNameStyle, - }], - kind: RenderedLineKind::OtherFileName, - }); - output.push(RenderedLine { - text: vec![StyledString { - text: "".to_string(), - style: Style::FileNameStyle, - }], - kind: RenderedLineKind::Annotations, - }); - } - } - } - - let mut next_line = lines_iter.next(); - while next_line.is_some() { - // Consume lines with annotations. - while let Some(line) = next_line { - if line.annotations.is_empty() { break; } - - let mut rendered_lines = self.render_line(line); - assert!(!rendered_lines.is_empty()); - if old_school { - match self.primary_span { - Some(span) => { - let lo = codemap.lookup_char_pos(span.lo); - let hi = codemap.lookup_char_pos(span.hi); - //Before each secondary line in old skool-mode, print the label - //as an old-style note - if !line.annotations[0].is_primary { - if let Some(ann) = line.annotations[0].label.clone() { - output.push(RenderedLine { - text: vec![StyledString { - text: lo.file.name.clone(), - style: Style::FileNameStyle, - }, StyledString { - text: format!(":{}:{}: {}:{} ", lo.line, lo.col.0 + 1, - hi.line, hi.col.0+1), - style: Style::LineAndColumn, - }, StyledString { - text: format!("note: "), - style: Style::OldSkoolNote, - }, StyledString { - text: format!("{}", ann), - style: Style::OldSkoolNoteText, - }], - kind: RenderedLineKind::Annotations, - }); - } - } - rendered_lines[0].text.insert(0, StyledString { - text: format!(":{} ", lo.line), - style: Style::LineAndColumn, - }); - rendered_lines[0].text.insert(0, StyledString { - text: lo.file.name.clone(), - style: Style::FileNameStyle, - }); - let gap_amount = - rendered_lines[0].text[0].text.len() + - rendered_lines[0].text[1].text.len(); - assert!(rendered_lines.len() >= 2, - "no annotations resulted from: {:?}", - line); - for i in 1..rendered_lines.len() { - rendered_lines[i].text.insert(0, StyledString { - text: vec![" "; gap_amount].join(""), - style: Style::NoStyle - }); - } - } - _ =>() - } - } - output.append(&mut rendered_lines); - next_line = lines_iter.next(); - } - - // Emit lines without annotations, but only if they are - // followed by a line with an annotation. - let unannotated_line = next_line; - let mut unannotated_lines = 0; - while let Some(line) = next_line { - if !line.annotations.is_empty() { break; } - unannotated_lines += 1; - next_line = lines_iter.next(); - } - if unannotated_lines > 1 { - output.push(RenderedLine::from((String::new(), - Style::NoStyle, - RenderedLineKind::Elision))); - } else if let Some(line) = unannotated_line { - output.append(&mut self.render_line(line)); - } - } - - output - } - - fn render_line(&self, line: &Line) -> Vec<RenderedLine> { - let old_school = check_old_skool(); - let source_string = self.file.get_line(line.line_index) - .unwrap_or(""); - let source_kind = RenderedLineKind::SourceText { - file: self.file.clone(), - line_index: line.line_index, - }; - - let mut styled_buffer = StyledBuffer::new(); - - // First create the source line we will highlight. - styled_buffer.append(0, &source_string, Style::Quotation); - - if line.annotations.is_empty() { - return styled_buffer.render(source_kind); - } - - // We want to display like this: - // - // vec.push(vec.pop().unwrap()); - // --- ^^^ _ previous borrow ends here - // | | - // | error occurs here - // previous borrow of `vec` occurs here - // - // But there are some weird edge cases to be aware of: - // - // vec.push(vec.pop().unwrap()); - // -------- - previous borrow ends here - // || - // |this makes no sense - // previous borrow of `vec` occurs here - // - // For this reason, we group the lines into "highlight lines" - // and "annotations lines", where the highlight lines have the `~`. - - //let mut highlight_line = Self::whitespace(&source_string); - - // Sort the annotations by (start, end col) - let mut annotations = line.annotations.clone(); - annotations.sort(); - - // Next, create the highlight line. - for annotation in &annotations { - if old_school { - for p in annotation.start_col .. annotation.end_col { - if p == annotation.start_col { - styled_buffer.putc(1, p, '^', - if annotation.is_primary { - Style::UnderlinePrimary - } else { - Style::OldSkoolNote - }); - } - else { - styled_buffer.putc(1, p, '~', - if annotation.is_primary { - Style::UnderlinePrimary - } else { - Style::OldSkoolNote - }); - } - } - } - else { - for p in annotation.start_col .. annotation.end_col { - if annotation.is_primary { - styled_buffer.putc(1, p, '^', Style::UnderlinePrimary); - if !annotation.is_minimized { - styled_buffer.set_style(0, p, Style::UnderlinePrimary); - } - } else { - styled_buffer.putc(1, p, '-', Style::UnderlineSecondary); - if !annotation.is_minimized { - styled_buffer.set_style(0, p, Style::UnderlineSecondary); - } - } - } - } - } - - // Now we are going to write labels in. To start, we'll exclude - // the annotations with no labels. - let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) = - annotations.into_iter() - .partition(|a| a.label.is_some()); - - // If there are no annotations that need text, we're done. - if labeled_annotations.is_empty() { - return styled_buffer.render(source_kind); - } - if old_school { - return styled_buffer.render(source_kind); - } - - // Now add the text labels. We try, when possible, to stick the rightmost - // annotation at the end of the highlight line: - // - // vec.push(vec.pop().unwrap()); - // --- --- - previous borrow ends here - // - // But sometimes that's not possible because one of the other - // annotations overlaps it. For example, from the test - // `span_overlap_label`, we have the following annotations - // (written on distinct lines for clarity): - // - // fn foo(x: u32) { - // -------------- - // - - // - // In this case, we can't stick the rightmost-most label on - // the highlight line, or we would get: - // - // fn foo(x: u32) { - // -------- x_span - // | - // fn_span - // - // which is totally weird. Instead we want: - // - // fn foo(x: u32) { - // -------------- - // | | - // | x_span - // fn_span - // - // which is...less weird, at least. In fact, in general, if - // the rightmost span overlaps with any other span, we should - // use the "hang below" version, so we can at least make it - // clear where the span *starts*. - let mut labeled_annotations = &labeled_annotations[..]; - match labeled_annotations.split_last().unwrap() { - (last, previous) => { - if previous.iter() - .chain(&unlabeled_annotations) - .all(|a| !overlaps(a, last)) - { - // append the label afterwards; we keep it in a separate - // string - let highlight_label: String = format!(" {}", last.label.as_ref().unwrap()); - if last.is_primary { - styled_buffer.append(1, &highlight_label, Style::LabelPrimary); - } else { - styled_buffer.append(1, &highlight_label, Style::LabelSecondary); - } - labeled_annotations = previous; - } - } - } - - // If that's the last annotation, we're done - if labeled_annotations.is_empty() { - return styled_buffer.render(source_kind); - } - - for (index, annotation) in labeled_annotations.iter().enumerate() { - // Leave: - // - 1 extra line - // - One line for each thing that comes after - let comes_after = labeled_annotations.len() - index - 1; - let blank_lines = 3 + comes_after; - - // For each blank line, draw a `|` at our column. The - // text ought to be long enough for this. - for index in 2..blank_lines { - if annotation.is_primary { - styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlinePrimary); - } else { - styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlineSecondary); - } - } - - if annotation.is_primary { - styled_buffer.puts(blank_lines, annotation.start_col, - annotation.label.as_ref().unwrap(), Style::LabelPrimary); - } else { - styled_buffer.puts(blank_lines, annotation.start_col, - annotation.label.as_ref().unwrap(), Style::LabelSecondary); - } - } - - styled_buffer.render(source_kind) - } -} - -fn prepend_prefixes(rendered_lines: &mut [RenderedLine]) { - let old_school = check_old_skool(); - if old_school { - return; - } - - let prefixes: Vec<_> = - rendered_lines.iter() - .map(|rl| rl.kind.prefix()) - .collect(); - - // find the max amount of spacing we need; add 1 to - // p.text.len() to leave space between the prefix and the - // source text - let padding_len = - prefixes.iter() - .map(|p| if p.text.len() == 0 { 0 } else { p.text.len() + 1 }) - .max() - .unwrap_or(0); - - // Ensure we insert at least one character of padding, so that the - // `-->` arrows can fit etc. - let padding_len = cmp::max(padding_len, 1); - - for (mut prefix, line) in prefixes.into_iter().zip(rendered_lines) { - let extra_spaces = (prefix.text.len() .. padding_len).map(|_| ' '); - prefix.text.extend(extra_spaces); - match line.kind { - RenderedLineKind::Elision => { - line.text.insert(0, prefix); - } - RenderedLineKind::PrimaryFileName => { - // --> filename - // 22 |> - // ^ - // padding_len - let dashes = (0..padding_len - 1).map(|_| ' ') - .chain(Some('-')) - .chain(Some('-')) - .chain(Some('>')) - .chain(Some(' ')); - line.text.insert(0, StyledString {text: dashes.collect(), - style: Style::LineNumber}) - } - RenderedLineKind::OtherFileName => { - // ::: filename - // 22 |> - // ^ - // padding_len - let dashes = (0..padding_len - 1).map(|_| ' ') - .chain(Some(':')) - .chain(Some(':')) - .chain(Some(':')) - .chain(Some(' ')); - line.text.insert(0, StyledString {text: dashes.collect(), - style: Style::LineNumber}) - } - _ => { - line.text.insert(0, prefix); - line.text.insert(1, StyledString {text: String::from("|> "), - style: Style::LineNumber}) - } - } - } -} - -fn trim_lines(rendered_lines: &mut [RenderedLine]) { - for line in rendered_lines { - while !line.text.is_empty() { - line.trim_last(); - if line.text.last().unwrap().text.is_empty() { - line.text.pop(); - } else { - break; - } - } - } -} - -impl Line { - fn new(line_index: usize) -> Line { - Line { - line_index: line_index, - annotations: vec![] - } - } - - fn push_annotation(&mut self, - start: CharPos, - end: CharPos, - is_primary: bool, - is_minimized: bool, - label: Option<String>) { - self.annotations.push(Annotation { - start_col: start.0, - end_col: end.0, - is_primary: is_primary, - is_minimized: is_minimized, - label: label, - }); - } -} - -fn overlaps(a1: &Annotation, - a2: &Annotation) - -> bool -{ - (a2.start_col .. a2.end_col).contains(a1.start_col) || - (a1.start_col .. a1.end_col).contains(a2.start_col) -} diff --git a/src/libsyntax/errors/snippet/test.rs b/src/libsyntax/errors/snippet/test.rs deleted file mode 100644 index 79e40a09165..00000000000 --- a/src/libsyntax/errors/snippet/test.rs +++ /dev/null @@ -1,597 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Code for testing annotated snippets. - -#![cfg(test)] - -use codemap::{BytePos, CodeMap, FileMap, NO_EXPANSION, Span}; -use std::rc::Rc; -use super::{RenderedLine, SnippetData}; - -/// Returns the span corresponding to the `n`th occurrence of -/// `substring` in `source_text`. -trait CodeMapExtension { - fn span_substr(&self, - file: &Rc<FileMap>, - source_text: &str, - substring: &str, - n: usize) - -> Span; -} - -impl CodeMapExtension for CodeMap { - fn span_substr(&self, - file: &Rc<FileMap>, - source_text: &str, - substring: &str, - n: usize) - -> Span - { - println!("span_substr(file={:?}/{:?}, substring={:?}, n={})", - file.name, file.start_pos, substring, n); - let mut i = 0; - let mut hi = 0; - loop { - let offset = source_text[hi..].find(substring).unwrap_or_else(|| { - panic!("source_text `{}` does not have {} occurrences of `{}`, only {}", - source_text, n, substring, i); - }); - let lo = hi + offset; - hi = lo + substring.len(); - if i == n { - let span = Span { - lo: BytePos(lo as u32 + file.start_pos.0), - hi: BytePos(hi as u32 + file.start_pos.0), - expn_id: NO_EXPANSION, - }; - assert_eq!(&self.span_to_snippet(span).unwrap()[..], - substring); - return span; - } - i += 1; - } - } -} - -fn splice(start: Span, end: Span) -> Span { - Span { - lo: start.lo, - hi: end.hi, - expn_id: NO_EXPANSION, - } -} - -fn make_string(lines: &[RenderedLine]) -> String { - lines.iter() - .flat_map(|rl| { - rl.text.iter() - .map(|s| &s.text[..]) - .chain(Some("\n")) - }) - .collect() -} - -#[test] -fn tab() { - let file_text = " -fn foo() { -\tbar; -} -"; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span_bar = cm.span_substr(&foo, file_text, "bar", 0); - - let mut snippet = SnippetData::new(cm, Some(span_bar)); - snippet.push(span_bar, true, None); - - let lines = snippet.render_lines(); - let text = make_string(&lines); - assert_eq!(&text[..], &" - --> foo.rs:3:2 - |> -3 |> \tbar; - |> \t^^^ -"[1..]); -} - -#[test] -fn one_line() { - let file_text = r#" -fn foo() { - vec.push(vec.pop().unwrap()); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0); - let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1); - let span_semi = cm.span_substr(&foo, file_text, ";", 0); - - let mut snippet = SnippetData::new(cm, None); - snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here"))); - snippet.push(span_vec1, false, Some(format!("error occurs here"))); - snippet.push(span_semi, false, Some(format!("previous borrow ends here"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - - let text: String = make_string(&lines); - - println!("text=\n{}", text); - assert_eq!(&text[..], &r#" - ::: foo.rs - |> -3 |> vec.push(vec.pop().unwrap()); - |> --- --- - previous borrow ends here - |> | | - |> | error occurs here - |> previous borrow of `vec` occurs here -"#[1..]); -} - -#[test] -fn two_files() { - let file_text_foo = r#" -fn foo() { - vec.push(vec.pop().unwrap()); -} -"#; - - let file_text_bar = r#" -fn bar() { - // these blank links here - // serve to ensure that the line numbers - // from bar.rs - // require more digits - - - - - - - - - - - vec.push(); - - // this line will get elided - - vec.pop().unwrap()); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo_map = cm.new_filemap_and_lines("foo.rs", None, file_text_foo); - let span_foo_vec0 = cm.span_substr(&foo_map, file_text_foo, "vec", 0); - let span_foo_vec1 = cm.span_substr(&foo_map, file_text_foo, "vec", 1); - let span_foo_semi = cm.span_substr(&foo_map, file_text_foo, ";", 0); - - let bar_map = cm.new_filemap_and_lines("bar.rs", None, file_text_bar); - let span_bar_vec0 = cm.span_substr(&bar_map, file_text_bar, "vec", 0); - let span_bar_vec1 = cm.span_substr(&bar_map, file_text_bar, "vec", 1); - let span_bar_semi = cm.span_substr(&bar_map, file_text_bar, ";", 0); - - let mut snippet = SnippetData::new(cm, Some(span_foo_vec1)); - snippet.push(span_foo_vec0, false, Some(format!("a"))); - snippet.push(span_foo_vec1, true, Some(format!("b"))); - snippet.push(span_foo_semi, false, Some(format!("c"))); - snippet.push(span_bar_vec0, false, Some(format!("d"))); - snippet.push(span_bar_vec1, false, Some(format!("e"))); - snippet.push(span_bar_semi, false, Some(format!("f"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - - let text: String = make_string(&lines); - - println!("text=\n{}", text); - - // Note that the `|>` remain aligned across both files: - assert_eq!(&text[..], &r#" - --> foo.rs:3:14 - |> -3 |> vec.push(vec.pop().unwrap()); - |> --- ^^^ - c - |> | | - |> | b - |> a - ::: bar.rs - |> -17 |> vec.push(); - |> --- - f - |> | - |> d -... -21 |> vec.pop().unwrap()); - |> --- e -"#[1..]); -} - -#[test] -fn multi_line() { - let file_text = r#" -fn foo() { - let name = find_id(&data, 22).unwrap(); - - // Add one more item we forgot to the vector. Silly us. - data.push(Data { name: format!("Hera"), id: 66 }); - - // Print everything out. - println!("Name: {:?}", name); - println!("Data: {:?}", data); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span_data0 = cm.span_substr(&foo, file_text, "data", 0); - let span_data1 = cm.span_substr(&foo, file_text, "data", 1); - let span_rbrace = cm.span_substr(&foo, file_text, "}", 3); - - let mut snippet = SnippetData::new(cm, None); - snippet.push(span_data0, false, Some(format!("immutable borrow begins here"))); - snippet.push(span_data1, false, Some(format!("mutable borrow occurs here"))); - snippet.push(span_rbrace, false, Some(format!("immutable borrow ends here"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - - let text: String = make_string(&lines); - - println!("text=\n{}", text); - assert_eq!(&text[..], &r#" - ::: foo.rs - |> -3 |> let name = find_id(&data, 22).unwrap(); - |> ---- immutable borrow begins here -... -6 |> data.push(Data { name: format!("Hera"), id: 66 }); - |> ---- mutable borrow occurs here -... -11 |> } - |> - immutable borrow ends here -"#[1..]); -} - -#[test] -fn overlapping() { - let file_text = r#" -fn foo() { - vec.push(vec.pop().unwrap()); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span0 = cm.span_substr(&foo, file_text, "vec.push", 0); - let span1 = cm.span_substr(&foo, file_text, "vec", 0); - let span2 = cm.span_substr(&foo, file_text, "ec.push", 0); - let span3 = cm.span_substr(&foo, file_text, "unwrap", 0); - - let mut snippet = SnippetData::new(cm, None); - snippet.push(span0, false, Some(format!("A"))); - snippet.push(span1, false, Some(format!("B"))); - snippet.push(span2, false, Some(format!("C"))); - snippet.push(span3, false, Some(format!("D"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - let text: String = make_string(&lines); - - println!("text=r#\"\n{}\".trim_left()", text); - assert_eq!(&text[..], &r#" - ::: foo.rs - |> -3 |> vec.push(vec.pop().unwrap()); - |> -------- ------ D - |> || - |> |C - |> A - |> B -"#[1..]); -} - -#[test] -fn one_line_out_of_order() { - let file_text = r#" -fn foo() { - vec.push(vec.pop().unwrap()); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0); - let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1); - let span_semi = cm.span_substr(&foo, file_text, ";", 0); - - // intentionally don't push the snippets left to right - let mut snippet = SnippetData::new(cm, None); - snippet.push(span_vec1, false, Some(format!("error occurs here"))); - snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here"))); - snippet.push(span_semi, false, Some(format!("previous borrow ends here"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - let text: String = make_string(&lines); - - println!("text=r#\"\n{}\".trim_left()", text); - assert_eq!(&text[..], &r#" - ::: foo.rs - |> -3 |> vec.push(vec.pop().unwrap()); - |> --- --- - previous borrow ends here - |> | | - |> | error occurs here - |> previous borrow of `vec` occurs here -"#[1..]); -} - -#[test] -fn elide_unnecessary_lines() { - let file_text = r#" -fn foo() { - let mut vec = vec![0, 1, 2]; - let mut vec2 = vec; - vec2.push(3); - vec2.push(4); - vec2.push(5); - vec2.push(6); - vec.push(7); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span_vec0 = cm.span_substr(&foo, file_text, "vec", 3); - let span_vec1 = cm.span_substr(&foo, file_text, "vec", 8); - - let mut snippet = SnippetData::new(cm, None); - snippet.push(span_vec0, false, Some(format!("`vec` moved here because it \ - has type `collections::vec::Vec<i32>`"))); - snippet.push(span_vec1, false, Some(format!("use of moved value: `vec`"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - let text: String = make_string(&lines); - println!("text=r#\"\n{}\".trim_left()", text); - assert_eq!(&text[..], &r#" - ::: foo.rs - |> -4 |> let mut vec2 = vec; - |> --- `vec` moved here because it has type `collections::vec::Vec<i32>` -... -9 |> vec.push(7); - |> --- use of moved value: `vec` -"#[1..]); -} - -#[test] -fn spans_without_labels() { - let file_text = r#" -fn foo() { - let mut vec = vec![0, 1, 2]; - let mut vec2 = vec; - vec2.push(3); - vec2.push(4); - vec2.push(5); - vec2.push(6); - vec.push(7); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut snippet = SnippetData::new(cm.clone(), None); - for i in 0..4 { - let span_veci = cm.span_substr(&foo, file_text, "vec", i); - snippet.push(span_veci, false, None); - } - - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("text=&r#\"\n{}\n\"#[1..]", text); - assert_eq!(text, &r#" - ::: foo.rs - |> -3 |> let mut vec = vec![0, 1, 2]; - |> --- --- -4 |> let mut vec2 = vec; - |> --- --- -"#[1..]); -} - -#[test] -fn span_long_selection() { - let file_text = r#" -impl SomeTrait for () { - fn foo(x: u32) { - // impl 1 - // impl 2 - // impl 3 - } -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut snippet = SnippetData::new(cm.clone(), None); - let fn_span = cm.span_substr(&foo, file_text, "fn", 0); - let rbrace_span = cm.span_substr(&foo, file_text, "}", 0); - snippet.push(splice(fn_span, rbrace_span), false, None); - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("r#\"\n{}\"", text); - assert_eq!(text, &r#" - ::: foo.rs - |> -3 |> fn foo(x: u32) { - |> - -"#[1..]); -} - -#[test] -fn span_overlap_label() { - // Test that we don't put `x_span` to the right of its highlight, - // since there is another highlight that overlaps it. - - let file_text = r#" - fn foo(x: u32) { - } -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut snippet = SnippetData::new(cm.clone(), None); - let fn_span = cm.span_substr(&foo, file_text, "fn foo(x: u32)", 0); - let x_span = cm.span_substr(&foo, file_text, "x", 0); - snippet.push(fn_span, false, Some(format!("fn_span"))); - snippet.push(x_span, false, Some(format!("x_span"))); - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("r#\"\n{}\"", text); - assert_eq!(text, &r#" - ::: foo.rs - |> -2 |> fn foo(x: u32) { - |> -------------- - |> | | - |> | x_span - |> fn_span -"#[1..]); -} - -#[test] -fn span_overlap_label2() { - // Test that we don't put `x_span` to the right of its highlight, - // since there is another highlight that overlaps it. In this - // case, the overlap is only at the beginning, but it's still - // better to show the beginning more clearly. - - let file_text = r#" - fn foo(x: u32) { - } -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut snippet = SnippetData::new(cm.clone(), None); - let fn_span = cm.span_substr(&foo, file_text, "fn foo(x", 0); - let x_span = cm.span_substr(&foo, file_text, "x: u32)", 0); - snippet.push(fn_span, false, Some(format!("fn_span"))); - snippet.push(x_span, false, Some(format!("x_span"))); - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("r#\"\n{}\"", text); - assert_eq!(text, &r#" - ::: foo.rs - |> -2 |> fn foo(x: u32) { - |> -------------- - |> | | - |> | x_span - |> fn_span -"#[1..]); -} - -#[test] -fn span_overlap_label3() { - // Test that we don't put `x_span` to the right of its highlight, - // since there is another highlight that overlaps it. In this - // case, the overlap is only at the beginning, but it's still - // better to show the beginning more clearly. - - let file_text = r#" - fn foo() { - let closure = || { - inner - }; - } -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut snippet = SnippetData::new(cm.clone(), None); - - let closure_span = { - let closure_start_span = cm.span_substr(&foo, file_text, "||", 0); - let closure_end_span = cm.span_substr(&foo, file_text, "}", 0); - splice(closure_start_span, closure_end_span) - }; - - let inner_span = cm.span_substr(&foo, file_text, "inner", 0); - - snippet.push(closure_span, false, Some(format!("foo"))); - snippet.push(inner_span, false, Some(format!("bar"))); - - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("r#\"\n{}\"", text); - assert_eq!(text, &r#" - ::: foo.rs - |> -3 |> let closure = || { - |> - foo -4 |> inner - |> ----- bar -"#[1..]); -} - -#[test] -fn span_empty() { - // In one of the unit tests, we found that the parser sometimes - // gives empty spans, and in particular it supplied an EOF span - // like this one, which points at the very end. We want to - // fallback gracefully in this case. - - let file_text = r#" -fn main() { - struct Foo; - - impl !Sync for Foo {} - - unsafe impl Send for &'static Foo { - // error: cross-crate traits with a default impl, like `core::marker::Send`, - // can only be implemented for a struct/enum type, not - // `&'static Foo` -}"#; - - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut rbrace_span = cm.span_substr(&foo, file_text, "}", 1); - rbrace_span.lo = rbrace_span.hi; - - let mut snippet = SnippetData::new(cm.clone(), Some(rbrace_span)); - snippet.push(rbrace_span, false, None); - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("r#\"\n{}\"", text); - assert_eq!(text, &r#" - --> foo.rs:11:2 - |> -11 |> } - |> - -"#[1..]); -} diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 9d467b4095a..3ee25634207 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -13,8 +13,8 @@ pub use self::SyntaxExtension::*; use ast; use ast::{Name, PatKind}; use attr::HasAttrs; -use codemap; -use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION}; +use codemap::{self, CodeMap, ExpnInfo}; +use syntax_pos::{Span, ExpnId, NO_EXPANSION}; use errors::DiagnosticBuilder; use ext; use ext::expand; diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 1d27cf5b0a1..fdf26263330 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -11,7 +11,8 @@ use abi::Abi; use ast::{self, Ident, Generics, Expr, BlockCheckMode, UnOp, PatKind}; use attr; -use codemap::{Span, respan, Spanned, DUMMY_SP, Pos}; +use syntax_pos::{Span, DUMMY_SP, Pos}; +use codemap::{respan, Spanned}; use ext::base::ExtCtxt; use parse::token::{self, keywords, InternedString}; use ptr::P; diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 126f39bcb0e..123d38c0006 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -18,8 +18,8 @@ use ext::mtwt; use ext::build::AstBuilder; use attr; use attr::{AttrMetaMethods, WithAttrs, ThinAttributesExt}; -use codemap; -use codemap::{Span, Spanned, ExpnInfo, ExpnId, NameAndSpan, MacroBang, MacroAttribute}; +use codemap::{Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; +use syntax_pos::{self, Span, ExpnId}; use config::StripUnconfigured; use ext::base::*; use feature_gate::{self, Features}; @@ -1053,7 +1053,7 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { result = expand_item(item, self); self.pop_mod_path(); } else { - let filename = if inner != codemap::DUMMY_SP { + let filename = if inner != syntax_pos::DUMMY_SP { Some(self.cx.parse_sess.codemap().span_to_filename(inner)) } else { None }; let orig_filename = replace(&mut self.cx.filename, filename); @@ -1242,7 +1242,7 @@ mod tests { use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig}; use ast; use ast::Name; - use codemap; + use syntax_pos; use ext::base::{ExtCtxt, DummyMacroLoader}; use ext::mtwt; use fold::Folder; @@ -1284,7 +1284,7 @@ mod tests { } impl<'v> Visitor<'v> for IdentFinder { - fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident){ + fn visit_ident(&mut self, _: syntax_pos::Span, id: ast::Ident){ self.ident_accumulator.push(id); } } diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index 871b0d4b1c0..db22eb7901c 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -9,7 +9,7 @@ // except according to those terms. use ast::{self, Arg, Arm, Block, Expr, Item, Pat, Stmt, TokenTree, Ty}; -use codemap::Span; +use syntax_pos::Span; use ext::base::ExtCtxt; use ext::base; use ext::build::AstBuilder; @@ -36,7 +36,8 @@ pub mod rt { use ast::TokenTree; pub use parse::new_parser_from_tts; - pub use codemap::{BytePos, Span, dummy_spanned, DUMMY_SP}; + pub use syntax_pos::{BytePos, Span, DUMMY_SP}; + pub use codemap::{dummy_spanned}; pub trait ToTokens { fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree>; diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index fd229d77966..a9b90dcbb7c 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -9,8 +9,7 @@ // except according to those terms. use ast; -use codemap::{Pos, Span}; -use codemap; +use syntax_pos::{self, Pos, Span}; use ext::base::*; use ext::base; use ext::build::AstBuilder; @@ -194,7 +193,7 @@ pub fn expand_include_bytes(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) // resolve a file-system path to an absolute file-system path (if it // isn't already) -fn res_rel_file(cx: &mut ExtCtxt, sp: codemap::Span, arg: &Path) -> PathBuf { +fn res_rel_file(cx: &mut ExtCtxt, sp: syntax_pos::Span, arg: &Path) -> PathBuf { // NB: relative paths are resolved relative to the compilation unit if !arg.is_absolute() { let mut cu = PathBuf::from(&cx.codemap().span_to_filename(sp)); diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index ca5eb8f8003..2230da10552 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -80,8 +80,8 @@ use self::TokenTreeOrTokenTreeVec::*; use ast; use ast::{TokenTree, Name, Ident}; -use codemap::{BytePos, mk_sp, Span, Spanned}; -use codemap; +use syntax_pos::{self, BytePos, mk_sp, Span}; +use codemap::Spanned; use errors::FatalError; use parse::lexer::*; //resolve bug? use parse::ParseSess; @@ -196,7 +196,7 @@ pub fn initial_matcher_pos(ms: Rc<Vec<TokenTree>>, sep: Option<Token>, lo: ByteP /// token tree it was derived from. pub enum NamedMatch { - MatchedSeq(Vec<Rc<NamedMatch>>, codemap::Span), + MatchedSeq(Vec<Rc<NamedMatch>>, syntax_pos::Span), MatchedNonterminal(Nonterminal) } @@ -204,7 +204,7 @@ pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>]) -> ParseResult<HashMap<Name, Rc<NamedMatch>>> { fn n_rec(p_s: &ParseSess, m: &TokenTree, res: &[Rc<NamedMatch>], ret_val: &mut HashMap<Name, Rc<NamedMatch>>, idx: &mut usize) - -> Result<(), (codemap::Span, String)> { + -> Result<(), (syntax_pos::Span, String)> { match *m { TokenTree::Sequence(_, ref seq) => { for next_m in &seq.tts { @@ -251,9 +251,9 @@ pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>]) pub enum ParseResult<T> { Success(T), /// Arm failed to match - Failure(codemap::Span, String), + Failure(syntax_pos::Span, String), /// Fatal error (malformed macro?). Abort compilation. - Error(codemap::Span, String) + Error(syntax_pos::Span, String) } pub type NamedParseResult = ParseResult<HashMap<Name, Rc<NamedMatch>>>; diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index fe98394c3e4..850fbb5addf 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -9,7 +9,7 @@ // except according to those terms. use ast::{self, TokenTree}; -use codemap::{Span, DUMMY_SP}; +use syntax_pos::{Span, DUMMY_SP}; use ext::base::{DummyResult, ExtCtxt, MacResult, SyntaxExtension}; use ext::base::{NormalTT, TTMacroExpander}; use ext::tt::macro_parser::{Success, Error, Failure}; diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs index 6b3b5ce9de9..8b5ecd10fcb 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/tt/transcribe.rs @@ -11,7 +11,7 @@ use self::LockstepIterSize::*; use ast; use ast::{TokenTree, Ident, Name}; -use codemap::{Span, DUMMY_SP}; +use syntax_pos::{Span, DUMMY_SP}; use errors::{Handler, DiagnosticBuilder}; use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal}; use parse::token::{DocComment, MatchNt, SubstNt}; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 550eb0a56d9..bb72dadcaf6 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -30,10 +30,10 @@ use ast::{NodeId, PatKind}; use ast; use attr; use attr::AttrMetaMethods; -use codemap::{CodeMap, Span}; +use codemap::CodeMap; +use syntax_pos::Span; use errors::Handler; -use visit; -use visit::{FnKind, Visitor}; +use visit::{self, FnKind, Visitor}; use parse::ParseSess; use parse::token::InternedString; diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 18661f3dc01..e8f9ddb2431 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -21,7 +21,8 @@ use ast::*; use ast; use attr::{ThinAttributes, ThinAttributesExt}; -use codemap::{respan, Span, Spanned}; +use syntax_pos::Span; +use codemap::{Spanned, respan}; use parse::token::{self, keywords}; use ptr::P; use util::small_vector::SmallVector; diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/json.rs index 93c6268ccae..dc9a5ee4664 100644 --- a/src/libsyntax/errors/json.rs +++ b/src/libsyntax/json.rs @@ -19,10 +19,10 @@ // FIXME spec the JSON output properly. - -use codemap::{self, MacroBacktrace, Span, SpanLabel, MultiSpan, CodeMap}; -use diagnostics::registry::Registry; -use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion}; +use codemap::CodeMap; +use syntax_pos::{self, MacroBacktrace, Span, SpanLabel, MultiSpan}; +use errors::registry::Registry; +use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper}; use errors::emitter::Emitter; use std::rc::Rc; @@ -34,7 +34,7 @@ use rustc_serialize::json::as_json; pub struct JsonEmitter { dst: Box<Write + Send>, registry: Option<Registry>, - cm: Rc<CodeMap>, + cm: Rc<CodeMapper + 'static>, } impl JsonEmitter { @@ -303,7 +303,7 @@ impl DiagnosticSpan { } impl DiagnosticSpanLine { - fn line_from_filemap(fm: &codemap::FileMap, + fn line_from_filemap(fm: &syntax_pos::FileMap, index: usize, h_start: usize, h_end: usize) @@ -354,12 +354,14 @@ impl DiagnosticCode { impl JsonEmitter { fn render(&self, render_span: &RenderSpan) -> Option<String> { + use std::borrow::Borrow; + match *render_span { RenderSpan::FullSpan(_) => { None } RenderSpan::Suggestion(ref suggestion) => { - Some(suggestion.splice_lines(&self.cm)) + Some(suggestion.splice_lines(self.cm.borrow())) } } } diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 420a41e03b9..ae61801c65b 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -33,7 +33,6 @@ #![feature(str_escape)] #![feature(unicode)] #![feature(question_mark)] -#![feature(range_contains)] extern crate serialize; extern crate term; @@ -41,9 +40,12 @@ extern crate libc; #[macro_use] extern crate log; #[macro_use] #[no_link] extern crate rustc_bitflags; extern crate rustc_unicode; +pub extern crate rustc_errors as errors; +extern crate syntax_pos; extern crate serialize as rustc_serialize; // used by deriving + // A variant of 'try!' that panics on an Err. This is used as a crutch on the // way towards a non-panic!-prone parser. It should be used for fatal parsing // errors; eventually we plan to convert all code using panictry to just use @@ -53,7 +55,7 @@ extern crate serialize as rustc_serialize; // used by deriving macro_rules! panictry { ($e:expr) => ({ use std::result::Result::{Ok, Err}; - use $crate::errors::FatalError; + use errors::FatalError; match $e { Ok(e) => e, Err(mut e) => { @@ -78,11 +80,10 @@ pub mod util { pub mod diagnostics { pub mod macros; pub mod plugin; - pub mod registry; pub mod metadata; } -pub mod errors; +pub mod json; pub mod syntax { pub use ext; diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index db643eb0df0..f6e94b7caea 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -10,7 +10,8 @@ use attr; use ast; -use codemap::{spanned, Spanned, mk_sp, Span}; +use syntax_pos::{mk_sp, Span}; +use codemap::{spanned, Spanned}; use parse::common::SeqSep; use parse::PResult; use parse::token; diff --git a/src/libsyntax/parse/lexer/comments.rs b/src/libsyntax/parse/lexer/comments.rs index 06d255d5c0f..5eb5605ea71 100644 --- a/src/libsyntax/parse/lexer/comments.rs +++ b/src/libsyntax/parse/lexer/comments.rs @@ -11,7 +11,8 @@ pub use self::CommentStyle::*; use ast; -use codemap::{BytePos, CharPos, CodeMap, Pos}; +use codemap::CodeMap; +use syntax_pos::{BytePos, CharPos, Pos}; use errors; use parse::lexer::is_block_doc_comment; use parse::lexer::{StringReader, TokenAndSpan}; diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index d78a81dec83..809f4daa361 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -9,8 +9,8 @@ // except according to those terms. use ast; -use codemap::{BytePos, CharPos, CodeMap, Pos, Span}; -use codemap; +use syntax_pos::{self, BytePos, CharPos, Pos, Span}; +use codemap::CodeMap; use errors::{FatalError, Handler, DiagnosticBuilder}; use ext::tt::transcribe::tt_next_token; use parse::token::{self, keywords, str_to_ident}; @@ -84,7 +84,7 @@ pub struct StringReader<'a> { pub col: CharPos, /// The last character to be read pub curr: Option<char>, - pub filemap: Rc<codemap::FileMap>, + pub filemap: Rc<syntax_pos::FileMap>, // cached: pub peek_tok: token::Token, pub peek_span: Span, @@ -162,7 +162,7 @@ impl<'a> Reader for TtReader<'a> { impl<'a> StringReader<'a> { /// For comments.rs, which hackily pokes into pos and curr pub fn new_raw<'b>(span_diagnostic: &'b Handler, - filemap: Rc<codemap::FileMap>) + filemap: Rc<syntax_pos::FileMap>) -> StringReader<'b> { if filemap.src.is_none() { span_diagnostic.bug(&format!("Cannot lex filemap \ @@ -181,7 +181,7 @@ impl<'a> StringReader<'a> { filemap: filemap, // dummy values; not read peek_tok: token::Eof, - peek_span: codemap::DUMMY_SP, + peek_span: syntax_pos::DUMMY_SP, source_text: source_text, fatal_errs: Vec::new(), }; @@ -190,7 +190,7 @@ impl<'a> StringReader<'a> { } pub fn new<'b>(span_diagnostic: &'b Handler, - filemap: Rc<codemap::FileMap>) + filemap: Rc<syntax_pos::FileMap>) -> StringReader<'b> { let mut sr = StringReader::new_raw(span_diagnostic, filemap); if let Err(_) = sr.advance_token() { @@ -217,12 +217,12 @@ impl<'a> StringReader<'a> { /// Report a fatal error spanning [`from_pos`, `to_pos`). fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError { - self.fatal_span(codemap::mk_sp(from_pos, to_pos), m) + self.fatal_span(syntax_pos::mk_sp(from_pos, to_pos), m) } /// Report a lexical error spanning [`from_pos`, `to_pos`). fn err_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) { - self.err_span(codemap::mk_sp(from_pos, to_pos), m) + self.err_span(syntax_pos::mk_sp(from_pos, to_pos), m) } /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an @@ -246,7 +246,7 @@ impl<'a> StringReader<'a> { for c in c.escape_default() { m.push(c) } - self.span_diagnostic.struct_span_fatal(codemap::mk_sp(from_pos, to_pos), &m[..]) + self.span_diagnostic.struct_span_fatal(syntax_pos::mk_sp(from_pos, to_pos), &m[..]) } /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an @@ -270,7 +270,7 @@ impl<'a> StringReader<'a> { for c in c.escape_default() { m.push(c) } - self.span_diagnostic.struct_span_err(codemap::mk_sp(from_pos, to_pos), &m[..]) + self.span_diagnostic.struct_span_err(syntax_pos::mk_sp(from_pos, to_pos), &m[..]) } /// Report a lexical error spanning [`from_pos`, `to_pos`), appending the @@ -294,11 +294,11 @@ impl<'a> StringReader<'a> { None => { if self.is_eof() { self.peek_tok = token::Eof; - self.peek_span = codemap::mk_sp(self.filemap.end_pos, self.filemap.end_pos); + self.peek_span = syntax_pos::mk_sp(self.filemap.end_pos, self.filemap.end_pos); } else { let start_bytepos = self.last_pos; self.peek_tok = self.next_token_inner()?; - self.peek_span = codemap::mk_sp(start_bytepos, self.last_pos); + self.peek_span = syntax_pos::mk_sp(start_bytepos, self.last_pos); }; } } @@ -473,7 +473,7 @@ impl<'a> StringReader<'a> { match self.curr { Some(c) => { if c.is_whitespace() { - self.span_diagnostic.span_err(codemap::mk_sp(self.last_pos, self.last_pos), + self.span_diagnostic.span_err(syntax_pos::mk_sp(self.last_pos, self.last_pos), "called consume_any_line_comment, but there \ was whitespace"); } @@ -524,13 +524,13 @@ impl<'a> StringReader<'a> { Some(TokenAndSpan { tok: tok, - sp: codemap::mk_sp(start_bpos, self.last_pos), + sp: syntax_pos::mk_sp(start_bpos, self.last_pos), }) }) } else { Some(TokenAndSpan { tok: token::Comment, - sp: codemap::mk_sp(start_bpos, self.last_pos), + sp: syntax_pos::mk_sp(start_bpos, self.last_pos), }) }; } @@ -563,7 +563,7 @@ impl<'a> StringReader<'a> { } return Some(TokenAndSpan { tok: token::Shebang(self.name_from(start)), - sp: codemap::mk_sp(start, self.last_pos), + sp: syntax_pos::mk_sp(start, self.last_pos), }); } } @@ -591,7 +591,7 @@ impl<'a> StringReader<'a> { } let c = Some(TokenAndSpan { tok: token::Whitespace, - sp: codemap::mk_sp(start_bpos, self.last_pos), + sp: syntax_pos::mk_sp(start_bpos, self.last_pos), }); debug!("scanning whitespace: {:?}", c); c @@ -653,7 +653,7 @@ impl<'a> StringReader<'a> { Some(TokenAndSpan { tok: tok, - sp: codemap::mk_sp(start_bpos, self.last_pos), + sp: syntax_pos::mk_sp(start_bpos, self.last_pos), }) }) } @@ -850,7 +850,7 @@ impl<'a> StringReader<'a> { let valid = if self.curr_is('{') { self.scan_unicode_escape(delim) && !ascii_only } else { - let span = codemap::mk_sp(start, self.last_pos); + let span = syntax_pos::mk_sp(start, self.last_pos); self.span_diagnostic .struct_span_err(span, "incorrect unicode escape sequence") .span_help(span, @@ -888,13 +888,13 @@ impl<'a> StringReader<'a> { }, c); if e == '\r' { - err.span_help(codemap::mk_sp(escaped_pos, last_pos), + err.span_help(syntax_pos::mk_sp(escaped_pos, last_pos), "this is an isolated carriage return; consider \ checking your editor and version control \ settings"); } if (e == '{' || e == '}') && !ascii_only { - err.span_help(codemap::mk_sp(escaped_pos, last_pos), + err.span_help(syntax_pos::mk_sp(escaped_pos, last_pos), "if used in a formatting string, curly braces \ are escaped with `{{` and `}}`"); } @@ -1677,7 +1677,8 @@ fn ident_continue(c: Option<char>) -> bool { mod tests { use super::*; - use codemap::{BytePos, CodeMap, Span, NO_EXPANSION}; + use syntax_pos::{BytePos, Span, NO_EXPANSION}; + use codemap::CodeMap; use errors; use parse::token; use parse::token::str_to_ident; @@ -1686,7 +1687,10 @@ mod tests { fn mk_sh(cm: Rc<CodeMap>) -> errors::Handler { // FIXME (#22405): Replace `Box::new` with `box` here when/if possible. - let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()), None, cm); + let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()), + None, + cm, + errors::snippet::FormatMode::EnvironmentSelected); errors::Handler::with_emitter(true, false, Box::new(emitter)) } @@ -1889,7 +1893,7 @@ mod tests { let mut lexer = setup(&cm, &sh, "// test\r\n/// test\r\n".to_string()); let comment = lexer.next_token(); assert_eq!(comment.tok, token::Comment); - assert_eq!(comment.sp, ::codemap::mk_sp(BytePos(0), BytePos(7))); + assert_eq!(comment.sp, ::syntax_pos::mk_sp(BytePos(0), BytePos(7))); assert_eq!(lexer.next_token().tok, token::Whitespace); assert_eq!(lexer.next_token().tok, token::DocComment(token::intern("/// test"))); diff --git a/src/libsyntax/parse/lexer/unicode_chars.rs b/src/libsyntax/parse/lexer/unicode_chars.rs index d337c78bee8..dab97d1d5a6 100644 --- a/src/libsyntax/parse/lexer/unicode_chars.rs +++ b/src/libsyntax/parse/lexer/unicode_chars.rs @@ -11,7 +11,7 @@ // Characters and their corresponding confusables were collected from // http://www.unicode.org/Public/security/revision-06/confusables.txt -use codemap::mk_sp as make_span; +use syntax_pos::mk_sp as make_span; use errors::DiagnosticBuilder; use super::StringReader; diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 2e4d46bc983..0796f298be1 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -11,7 +11,8 @@ //! The main parser interface use ast; -use codemap::{self, Span, CodeMap, FileMap}; +use codemap::CodeMap; +use syntax_pos::{self, Span, FileMap}; use errors::{Handler, ColorConfig, DiagnosticBuilder}; use parse::parser::Parser; use parse::token::InternedString; @@ -211,8 +212,8 @@ pub fn filemap_to_parser<'a>(sess: &'a ParseSess, let end_pos = filemap.end_pos; let mut parser = tts_to_parser(sess, filemap_to_tts(sess, filemap), cfg); - if parser.token == token::Eof && parser.span == codemap::DUMMY_SP { - parser.span = codemap::mk_sp(end_pos, end_pos); + if parser.token == token::Eof && parser.span == syntax_pos::DUMMY_SP { + parser.span = syntax_pos::mk_sp(end_pos, end_pos); } parser @@ -661,7 +662,8 @@ pub fn integer_lit(s: &str, mod tests { use super::*; use std::rc::Rc; - use codemap::{Span, BytePos, Pos, Spanned, NO_EXPANSION}; + use syntax_pos::{Span, BytePos, Pos, NO_EXPANSION}; + use codemap::Spanned; use ast::{self, TokenTree, PatKind}; use abi::Abi; use attr::{first_attr_value_str_by_name, AttrMetaMethods}; @@ -673,7 +675,7 @@ mod tests { use util::parser_testing::{string_to_tts, string_to_parser}; use util::parser_testing::{string_to_expr, string_to_item, string_to_stmt}; - // produce a codemap::span + // produce a syntax_pos::span fn sp(a: u32, b: u32) -> Span { Span {lo: BytePos(a), hi: BytePos(b), expn_id: NO_EXPANSION} } diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs index 75f1ac49c9a..a1d7ddcdf4b 100644 --- a/src/libsyntax/parse/obsolete.rs +++ b/src/libsyntax/parse/obsolete.rs @@ -13,7 +13,7 @@ //! //! Obsolete syntax that becomes too hard to parse can be removed. -use codemap::Span; +use syntax_pos::Span; use parse::parser; /// The specific types of unsupported syntax diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index de41ab5e189..99eddce3645 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -40,7 +40,8 @@ use ast::{Visibility, WhereClause}; use attr::{ThinAttributes, ThinAttributesExt, AttributesExt}; use ast::{BinOpKind, UnOp}; use ast; -use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp, CodeMap}; +use codemap::{self, CodeMap, Spanned, spanned}; +use syntax_pos::{self, Span, BytePos, mk_sp}; use errors::{self, DiagnosticBuilder}; use ext::tt::macro_parser; use parse; @@ -344,7 +345,7 @@ impl<'a> Parser<'a> { { let tok0 = rdr.real_token(); let span = tok0.sp; - let filename = if span != codemap::DUMMY_SP { + let filename = if span != syntax_pos::DUMMY_SP { Some(sess.codemap().span_to_filename(span)) } else { None }; let placeholder = TokenAndSpan { @@ -3525,7 +3526,7 @@ impl<'a> Parser<'a> { } /// Parse the fields of a struct-like pattern - fn parse_pat_fields(&mut self) -> PResult<'a, (Vec<codemap::Spanned<ast::FieldPat>> , bool)> { + fn parse_pat_fields(&mut self) -> PResult<'a, (Vec<codemap::Spanned<ast::FieldPat>>, bool)> { let mut fields = Vec::new(); let mut etc = false; let mut first = true; @@ -3595,9 +3596,9 @@ impl<'a> Parser<'a> { }; fields.push(codemap::Spanned { span: mk_sp(lo, hi), - node: ast::FieldPat { ident: fieldname, - pat: subpat, - is_shorthand: is_shorthand }}); + node: ast::FieldPat { ident: fieldname, + pat: subpat, + is_shorthand: is_shorthand }}); } return Ok((fields, etc)); } @@ -3688,7 +3689,7 @@ impl<'a> Parser<'a> { SeqSep::none(), |p| p.parse_token_tree())?; let mac = Mac_ { path: path, tts: tts, ctxt: EMPTY_CTXT }; pat = PatKind::Mac(codemap::Spanned {node: mac, - span: mk_sp(lo, self.last_span.hi)}); + span: mk_sp(lo, self.last_span.hi)}); } else { // Parse ident @ pat // This can give false positives and parse nullary enums, @@ -4935,8 +4936,8 @@ impl<'a> Parser<'a> { |p| p.parse_token_tree())?; let m_ = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT }; let m: ast::Mac = codemap::Spanned { node: m_, - span: mk_sp(lo, - self.last_span.hi) }; + span: mk_sp(lo, + self.last_span.hi) }; if delim != token::Brace { self.expect(&token::Semi)? } @@ -5274,7 +5275,7 @@ impl<'a> Parser<'a> { return Err(self.fatal(&format!("expected item, found `{}`", token_str))); } - let hi = if self.span == codemap::DUMMY_SP { + let hi = if self.span == syntax_pos::DUMMY_SP { inner_lo } else { self.last_span.hi @@ -6020,8 +6021,8 @@ impl<'a> Parser<'a> { // single-variant-enum... : let m = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT }; let m: ast::Mac = codemap::Spanned { node: m, - span: mk_sp(mac_lo, - self.last_span.hi) }; + span: mk_sp(mac_lo, + self.last_span.hi) }; if delim != token::Brace { if !self.eat(&token::Semi) { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 1fefc53af63..9fc5e1089c0 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -18,7 +18,8 @@ use attr::ThinAttributesExt; use util::parser::AssocOp; use attr; use attr::{AttrMetaMethods, AttributeMethods}; -use codemap::{self, CodeMap, BytePos}; +use codemap::{self, CodeMap}; +use syntax_pos::{self, BytePos}; use errors; use parse::token::{self, keywords, BinOpToken, Token, InternedString}; use parse::lexer::comments; @@ -842,11 +843,11 @@ impl<'a> State<'a> { self.end() // close the head-box } - pub fn bclose_(&mut self, span: codemap::Span, + pub fn bclose_(&mut self, span: syntax_pos::Span, indented: usize) -> io::Result<()> { self.bclose_maybe_open(span, indented, true) } - pub fn bclose_maybe_open(&mut self, span: codemap::Span, + pub fn bclose_maybe_open(&mut self, span: syntax_pos::Span, indented: usize, close_box: bool) -> io::Result<()> { try!(self.maybe_print_comment(span.hi)); try!(self.break_offset_if_not_bol(1, -(indented as isize))); @@ -856,7 +857,7 @@ impl<'a> State<'a> { } Ok(()) } - pub fn bclose(&mut self, span: codemap::Span) -> io::Result<()> { + pub fn bclose(&mut self, span: syntax_pos::Span) -> io::Result<()> { self.bclose_(span, INDENT_UNIT) } @@ -900,7 +901,7 @@ impl<'a> State<'a> { mut op: F, mut get_span: G) -> io::Result<()> where F: FnMut(&mut State, &T) -> io::Result<()>, - G: FnMut(&T) -> codemap::Span, + G: FnMut(&T) -> syntax_pos::Span, { try!(self.rbox(0, b)); let len = elts.len(); @@ -1352,7 +1353,7 @@ impl<'a> State<'a> { pub fn print_enum_def(&mut self, enum_definition: &ast::EnumDef, generics: &ast::Generics, ident: ast::Ident, - span: codemap::Span, + span: syntax_pos::Span, visibility: &ast::Visibility) -> io::Result<()> { try!(self.head(&visibility_qualified(visibility, "enum"))); try!(self.print_ident(ident)); @@ -1364,7 +1365,7 @@ impl<'a> State<'a> { pub fn print_variants(&mut self, variants: &[ast::Variant], - span: codemap::Span) -> io::Result<()> { + span: syntax_pos::Span) -> io::Result<()> { try!(self.bopen()); for v in variants { try!(self.space_if_not_bol()); @@ -1393,7 +1394,7 @@ impl<'a> State<'a> { struct_def: &ast::VariantData, generics: &ast::Generics, ident: ast::Ident, - span: codemap::Span, + span: syntax_pos::Span, print_finalizer: bool) -> io::Result<()> { try!(self.print_ident(ident)); try!(self.print_generics(generics)); @@ -3010,7 +3011,7 @@ impl<'a> State<'a> { self.end() } - pub fn maybe_print_trailing_comment(&mut self, span: codemap::Span, + pub fn maybe_print_trailing_comment(&mut self, span: syntax_pos::Span, next_pos: Option<BytePos>) -> io::Result<()> { let cm = match self.cm { @@ -3115,6 +3116,7 @@ mod tests { use ast; use codemap; use parse::token; + use syntax_pos; #[test] fn test_fun_to_string() { @@ -3122,7 +3124,7 @@ mod tests { let decl = ast::FnDecl { inputs: Vec::new(), - output: ast::FunctionRetTy::Default(codemap::DUMMY_SP), + output: ast::FunctionRetTy::Default(syntax_pos::DUMMY_SP), variadic: false }; let generics = ast::Generics::default(); @@ -3136,7 +3138,7 @@ mod tests { fn test_variant_to_string() { let ident = token::str_to_ident("principal_skinner"); - let var = codemap::respan(codemap::DUMMY_SP, ast::Variant_ { + let var = codemap::respan(syntax_pos::DUMMY_SP, ast::Variant_ { name: ident, attrs: Vec::new(), // making this up as I go.... ? diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 8834c026067..d1454ab06cb 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -10,8 +10,8 @@ use ast; use attr; -use codemap::{DUMMY_SP, Span, ExpnInfo, NameAndSpan, MacroAttribute}; -use codemap; +use syntax_pos::{DUMMY_SP, Span}; +use codemap::{self, ExpnInfo, NameAndSpan, MacroAttribute}; use parse::token::{intern, InternedString, keywords}; use parse::{token, ParseSess}; use ptr::P; diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index ca6ed76d549..cc3fff09617 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -12,6 +12,7 @@ #![allow(dead_code)] #![allow(unused_imports)] + use self::HasTestSignature::*; use std::iter; @@ -20,9 +21,12 @@ use std::mem; use std::vec; use attr::AttrMetaMethods; use attr; -use codemap::{DUMMY_SP, Span, ExpnInfo, NameAndSpan, MacroAttribute}; -use codemap; +use syntax_pos::{self, DUMMY_SP, NO_EXPANSION, Span, FileMap, BytePos}; +use std::rc::Rc; + +use codemap::{self, CodeMap, ExpnInfo, NameAndSpan, MacroAttribute}; use errors; +use errors::snippet::{RenderedLine, SnippetData}; use config; use entry::{self, EntryPointType}; use ext::base::{ExtCtxt, DummyMacroLoader}; diff --git a/src/libsyntax/util/node_count.rs b/src/libsyntax/util/node_count.rs index 919dd84b117..ad6145f8618 100644 --- a/src/libsyntax/util/node_count.rs +++ b/src/libsyntax/util/node_count.rs @@ -12,7 +12,7 @@ use visit::*; use ast::*; -use codemap::Span; +use syntax_pos::Span; pub struct NodeCounter { pub count: usize, diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 5ec4c3eef31..113c0d0293b 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -26,7 +26,8 @@ use abi::Abi; use ast::*; use attr::ThinAttributesExt; -use codemap::{Span, Spanned}; +use syntax_pos::Span; +use codemap::Spanned; #[derive(Copy, Clone, PartialEq, Eq)] pub enum FnKind<'a> { |
