#![feature(const_fn)]
#![feature(custom_attribute)]
+#![feature(i128_type)]
#![feature(optin_builtin_traits)]
#![allow(unused_attributes)]
#![feature(specialization)]
-#![cfg_attr(stage0, unstable(feature = "rustc_private", issue = "27812"))]
-#![cfg_attr(stage0, feature(rustc_private))]
-#![cfg_attr(stage0, feature(staged_api))]
-
+use std::borrow::Cow;
use std::cell::{Cell, RefCell};
+use std::cmp;
+use std::fmt;
+use std::hash::Hasher;
use std::ops::{Add, Sub};
use std::rc::Rc;
-use std::cmp;
-use std::fmt;
+use rustc_data_structures::stable_hasher::StableHasher;
+
+extern crate rustc_data_structures;
use serialize::{Encodable, Decodable, Encoder, Decoder};
if self.source_equal(&DUMMY_SP) { other } else { self }
}
+ /// Return true if `self` fully encloses `other`.
pub fn contains(self, other: Span) -> bool {
self.lo <= other.lo && other.hi <= self.hi
}
}
}
+ /// Check if a span is "internal" to a macro in which `unsafe`
+ /// can be used without triggering the `unsafe_code` lint
+ // (that is, a macro marked with `#[allow_internal_unsafe]`).
+ pub fn allows_unsafe(&self) -> bool {
+ match self.ctxt.outer().expn_info() {
+ Some(info) => info.callee.allow_internal_unsafe,
+ None => false,
+ }
+ }
+
pub fn macro_backtrace(mut self) -> Vec<MacroBacktrace> {
let mut prev_span = DUMMY_SP;
let mut result = vec![];
if !info.call_site.source_equal(&prev_span) {
result.push(MacroBacktrace {
call_site: info.call_site,
- macro_decl_name: macro_decl_name,
- def_site_span: def_site_span,
+ macro_decl_name,
+ def_site_span,
});
}
result
}
+ /// Return a `Span` that would enclose both `self` and `end`.
pub fn to(self, end: Span) -> Span {
- // FIXME(jseyfried): self.ctxt should always equal end.ctxt here (c.f. issue #23480)
- if end.ctxt == SyntaxContext::empty() {
- Span { lo: self.lo, ..end }
- } else {
- Span { hi: end.hi, ..self }
+ Span {
+ lo: cmp::min(self.lo, end.lo),
+ hi: cmp::max(self.hi, end.hi),
+ // FIXME(jseyfried): self.ctxt should always equal end.ctxt here (c.f. issue #23480)
+ ctxt: if self.ctxt == SyntaxContext::empty() {
+ end.ctxt
+ } else {
+ self.ctxt
+ },
}
}
+ /// Return a `Span` between the end of `self` to the beginning of `end`.
pub fn between(self, end: Span) -> Span {
Span {
lo: self.hi,
}
}
+ /// Return a `Span` between the beginning of `self` to the beginning of `end`.
pub fn until(self, end: Span) -> Span {
Span {
lo: self.lo,
pub label: Option<String>,
}
+impl Default for Span {
+ fn default() -> Self {
+ DUMMY_SP
+ }
+}
+
impl serialize::UseSpecializedEncodable for Span {
fn default_encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("Span", 2, |s| {
&self.primary_spans
}
- /// Replaces all occurances of one Span with another. Used to move Spans in areas that don't
+ /// Replaces all occurrences of one Span with another. Used to move Spans in areas that don't
/// display well (like std macros). Returns true if replacements occurred.
pub fn replace(&mut self, before: Span, after: Span) -> bool {
let mut replacements_occurred = false;
for &(span, ref label) in &self.span_labels {
span_labels.push(SpanLabel {
- span: 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,
+ span,
is_primary: true,
label: None
});
pub bytes: usize,
}
+/// The state of the lazy external source loading mechanism of a FileMap.
+#[derive(PartialEq, Eq, Clone)]
+pub enum ExternalSource {
+ /// The external source has been loaded already.
+ Present(String),
+ /// No attempt has been made to load the external source.
+ AbsentOk,
+ /// A failed attempt has been made to load the external source.
+ AbsentErr,
+ /// No external source has to be loaded, since the FileMap represents a local crate.
+ Unneeded,
+}
+
+impl ExternalSource {
+ pub fn is_absent(&self) -> bool {
+ match *self {
+ ExternalSource::Present(_) => false,
+ _ => true,
+ }
+ }
+
+ pub fn get_source(&self) -> Option<&str> {
+ match *self {
+ ExternalSource::Present(ref src) => Some(src),
+ _ => None,
+ }
+ }
+}
+
/// A single source in the CodeMap.
#[derive(Clone)]
pub struct FileMap {
pub crate_of_origin: u32,
/// The complete source code
pub src: Option<Rc<String>>,
+ /// The source code's hash
+ pub src_hash: u128,
+ /// The external source code (used for external crates, which will have a `None`
+ /// value as `self.src`.
+ pub external_src: RefCell<ExternalSource>,
/// The start position of this source in the CodeMap
pub start_pos: BytePos,
/// The end position of this source in the CodeMap
impl Encodable for FileMap {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
- s.emit_struct("FileMap", 6, |s| {
+ s.emit_struct("FileMap", 7, |s| {
s.emit_struct_field("name", 0, |s| self.name.encode(s))?;
s.emit_struct_field("name_was_remapped", 1, |s| self.name_was_remapped.encode(s))?;
+ s.emit_struct_field("src_hash", 6, |s| self.src_hash.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 name: String = d.read_struct_field("name", 0, |d| Decodable::decode(d))?;
let name_was_remapped: bool =
d.read_struct_field("name_was_remapped", 1, |d| Decodable::decode(d))?;
- let start_pos: BytePos = d.read_struct_field("start_pos", 2, |d| Decodable::decode(d))?;
+ let src_hash: u128 =
+ d.read_struct_field("src_hash", 6, |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 multibyte_chars: Vec<MultiByteChar> =
d.read_struct_field("multibyte_chars", 5, |d| Decodable::decode(d))?;
Ok(FileMap {
- name: name,
- name_was_remapped: name_was_remapped,
+ name,
+ name_was_remapped,
// `crate_of_origin` has to be set by the importer.
// This value matches up with rustc::hir::def_id::INVALID_CRATE.
// That constant is not available here unfortunately :(
crate_of_origin: ::std::u32::MAX - 1,
- start_pos: start_pos,
- end_pos: end_pos,
+ start_pos,
+ end_pos,
src: None,
+ src_hash,
+ external_src: RefCell::new(ExternalSource::AbsentOk),
lines: RefCell::new(lines),
multibyte_chars: RefCell::new(multibyte_chars)
})
}
impl FileMap {
+ pub fn new(name: FileName,
+ name_was_remapped: bool,
+ mut src: String,
+ start_pos: BytePos) -> FileMap {
+ remove_bom(&mut src);
+
+ let mut hasher: StableHasher<u128> = StableHasher::new();
+ hasher.write(src.as_bytes());
+ let src_hash = hasher.finish();
+
+ let end_pos = start_pos.to_usize() + src.len();
+
+ FileMap {
+ name,
+ name_was_remapped,
+ crate_of_origin: 0,
+ src: Some(Rc::new(src)),
+ src_hash,
+ external_src: RefCell::new(ExternalSource::Unneeded),
+ start_pos,
+ end_pos: Pos::from_usize(end_pos),
+ lines: RefCell::new(Vec::new()),
+ multibyte_chars: RefCell::new(Vec::new()),
+ }
+ }
+
/// EFFECT: register a start-of-line offset in the
/// table of line-beginnings.
/// UNCHECKED INVARIANT: these offsets must be added in the right
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
- }
- })
+ /// Add externally loaded source.
+ /// If the hash of the input doesn't match or no input is supplied via None,
+ /// it is interpreted as an error and the corresponding enum variant is set.
+ /// The return value signifies whether some kind of source is present.
+ pub fn add_external_src<F>(&self, get_src: F) -> bool
+ where F: FnOnce() -> Option<String>
+ {
+ if *self.external_src.borrow() == ExternalSource::AbsentOk {
+ let src = get_src();
+ let mut external_src = self.external_src.borrow_mut();
+ if let Some(src) = src {
+ let mut hasher: StableHasher<u128> = StableHasher::new();
+ hasher.write(src.as_bytes());
+
+ if hasher.finish() == self.src_hash {
+ *external_src = ExternalSource::Present(src);
+ return true;
+ }
+ } else {
+ *external_src = ExternalSource::AbsentErr;
+ }
+
+ false
+ } else {
+ self.src.is_some() || self.external_src.borrow().get_source().is_some()
+ }
+ }
+
+ /// Get a line from the list of pre-computed line-beginnings.
+ /// The line number here is 0-based.
+ pub fn get_line(&self, line_number: usize) -> Option<Cow<str>> {
+ fn get_until_newline(src: &str, begin: usize) -> &str {
+ // 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
+ }
+
+ let lines = self.lines.borrow();
+ let line = if let Some(line) = lines.get(line_number) {
+ line
+ } else {
+ return None;
+ };
+ let begin: BytePos = *line - self.start_pos;
+ let begin = begin.to_usize();
+
+ if let Some(ref src) = self.src {
+ Some(Cow::from(get_until_newline(src, begin)))
+ } else if let Some(src) = self.external_src.borrow().get_source() {
+ Some(Cow::Owned(String::from(get_until_newline(src, begin))))
+ } else {
+ None
}
}
pub fn record_multibyte_char(&self, pos: BytePos, bytes: usize) {
assert!(bytes >=2 && bytes <= 4);
let mbc = MultiByteChar {
- pos: pos,
- bytes: bytes,
+ pos,
+ bytes,
};
self.multibyte_chars.borrow_mut().push(mbc);
}
}
}
+/// Remove utf-8 BOM if any.
+fn remove_bom(src: &mut String) {
+ if src.starts_with("\u{feff}") {
+ src.drain(..3);
+ }
+}
+
// _____________________________________________________________________________
// Pos, BytePos, CharPos
//
thread_local!(pub static SPAN_DEBUG: Cell<fn(Span, &mut fmt::Formatter) -> fmt::Result> =
Cell::new(default_span_debug));
+#[derive(Debug)]
pub struct MacroBacktrace {
/// span where macro was applied to generate this code
pub call_site: Span,