diff options
| author | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2019-12-29 16:39:31 +0300 |
|---|---|---|
| committer | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2019-12-30 19:18:16 +0300 |
| commit | b683de4ad79242fdeebcae2afefb72c1530babe9 (patch) | |
| tree | e46daf86fae68f2246b1dd80500f4a504d452b84 /src/libsyntax_pos | |
| parent | 0fb43801368ae8b5931583f813071120bed55c35 (diff) | |
| download | rust-b683de4ad79242fdeebcae2afefb72c1530babe9.tar.gz rust-b683de4ad79242fdeebcae2afefb72c1530babe9.zip | |
Rename directories for some crates from `syntax_x` to `rustc_x`
`syntax_expand` -> `rustc_expand` `syntax_pos` -> `rustc_span` `syntax_ext` -> `rustc_builtin_macros`
Diffstat (limited to 'src/libsyntax_pos')
| -rw-r--r-- | src/libsyntax_pos/Cargo.toml | 21 | ||||
| -rw-r--r-- | src/libsyntax_pos/analyze_source_file.rs | 274 | ||||
| -rw-r--r-- | src/libsyntax_pos/analyze_source_file/tests.rs | 142 | ||||
| -rw-r--r-- | src/libsyntax_pos/caching_source_map_view.rs | 108 | ||||
| -rw-r--r-- | src/libsyntax_pos/edition.rs | 83 | ||||
| -rw-r--r-- | src/libsyntax_pos/fatal_error.rs | 26 | ||||
| -rw-r--r-- | src/libsyntax_pos/hygiene.rs | 850 | ||||
| -rw-r--r-- | src/libsyntax_pos/lib.rs | 1672 | ||||
| -rw-r--r-- | src/libsyntax_pos/source_map.rs | 984 | ||||
| -rw-r--r-- | src/libsyntax_pos/source_map/tests.rs | 216 | ||||
| -rw-r--r-- | src/libsyntax_pos/span_encoding.rs | 140 | ||||
| -rw-r--r-- | src/libsyntax_pos/symbol.rs | 1213 | ||||
| -rw-r--r-- | src/libsyntax_pos/symbol/tests.rs | 25 | ||||
| -rw-r--r-- | src/libsyntax_pos/tests.rs | 40 |
14 files changed, 0 insertions, 5794 deletions
diff --git a/src/libsyntax_pos/Cargo.toml b/src/libsyntax_pos/Cargo.toml deleted file mode 100644 index 2cac76085d2..00000000000 --- a/src/libsyntax_pos/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "syntax_pos" -version = "0.0.0" -edition = "2018" - -[lib] -name = "syntax_pos" -path = "lib.rs" -doctest = false - -[dependencies] -rustc_serialize = { path = "../libserialize", package = "serialize" } -rustc_macros = { path = "../librustc_macros" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_index = { path = "../librustc_index" } -arena = { path = "../libarena" } -scoped-tls = "1.0" -unicode-width = "0.1.4" -cfg-if = "0.1.2" -log = "0.4" diff --git a/src/libsyntax_pos/analyze_source_file.rs b/src/libsyntax_pos/analyze_source_file.rs deleted file mode 100644 index b4beb3dc376..00000000000 --- a/src/libsyntax_pos/analyze_source_file.rs +++ /dev/null @@ -1,274 +0,0 @@ -use super::*; -use unicode_width::UnicodeWidthChar; - -#[cfg(test)] -mod tests; - -/// Finds all newlines, multi-byte characters, and non-narrow characters in a -/// SourceFile. -/// -/// This function will use an SSE2 enhanced implementation if hardware support -/// is detected at runtime. -pub fn analyze_source_file( - src: &str, - source_file_start_pos: BytePos, -) -> (Vec<BytePos>, Vec<MultiByteChar>, Vec<NonNarrowChar>) { - let mut lines = vec![source_file_start_pos]; - let mut multi_byte_chars = vec![]; - let mut non_narrow_chars = vec![]; - - // Calls the right implementation, depending on hardware support available. - analyze_source_file_dispatch( - src, - source_file_start_pos, - &mut lines, - &mut multi_byte_chars, - &mut non_narrow_chars, - ); - - // The code above optimistically registers a new line *after* each \n - // it encounters. If that point is already outside the source_file, remove - // it again. - if let Some(&last_line_start) = lines.last() { - let source_file_end = source_file_start_pos + BytePos::from_usize(src.len()); - assert!(source_file_end >= last_line_start); - if last_line_start == source_file_end { - lines.pop(); - } - } - - (lines, multi_byte_chars, non_narrow_chars) -} - -cfg_if::cfg_if! { - if #[cfg(all(any(target_arch = "x86", target_arch = "x86_64")))] { - fn analyze_source_file_dispatch(src: &str, - source_file_start_pos: BytePos, - lines: &mut Vec<BytePos>, - multi_byte_chars: &mut Vec<MultiByteChar>, - non_narrow_chars: &mut Vec<NonNarrowChar>) { - if is_x86_feature_detected!("sse2") { - unsafe { - analyze_source_file_sse2(src, - source_file_start_pos, - lines, - multi_byte_chars, - non_narrow_chars); - } - } else { - analyze_source_file_generic(src, - src.len(), - source_file_start_pos, - lines, - multi_byte_chars, - non_narrow_chars); - - } - } - - /// Checks 16 byte chunks of text at a time. If the chunk contains - /// something other than printable ASCII characters and newlines, the - /// function falls back to the generic implementation. Otherwise it uses - /// SSE2 intrinsics to quickly find all newlines. - #[target_feature(enable = "sse2")] - unsafe fn analyze_source_file_sse2(src: &str, - output_offset: BytePos, - lines: &mut Vec<BytePos>, - multi_byte_chars: &mut Vec<MultiByteChar>, - non_narrow_chars: &mut Vec<NonNarrowChar>) { - #[cfg(target_arch = "x86")] - use std::arch::x86::*; - #[cfg(target_arch = "x86_64")] - use std::arch::x86_64::*; - - const CHUNK_SIZE: usize = 16; - - let src_bytes = src.as_bytes(); - - let chunk_count = src.len() / CHUNK_SIZE; - - // This variable keeps track of where we should start decoding a - // chunk. If a multi-byte character spans across chunk boundaries, - // we need to skip that part in the next chunk because we already - // handled it. - let mut intra_chunk_offset = 0; - - for chunk_index in 0 .. chunk_count { - let ptr = src_bytes.as_ptr() as *const __m128i; - // We don't know if the pointer is aligned to 16 bytes, so we - // use `loadu`, which supports unaligned loading. - let chunk = _mm_loadu_si128(ptr.offset(chunk_index as isize)); - - // For character in the chunk, see if its byte value is < 0, which - // indicates that it's part of a UTF-8 char. - let multibyte_test = _mm_cmplt_epi8(chunk, _mm_set1_epi8(0)); - // Create a bit mask from the comparison results. - let multibyte_mask = _mm_movemask_epi8(multibyte_test); - - // If the bit mask is all zero, we only have ASCII chars here: - if multibyte_mask == 0 { - assert!(intra_chunk_offset == 0); - - // Check if there are any control characters in the chunk. All - // control characters that we can encounter at this point have a - // byte value less than 32 or ... - let control_char_test0 = _mm_cmplt_epi8(chunk, _mm_set1_epi8(32)); - let control_char_mask0 = _mm_movemask_epi8(control_char_test0); - - // ... it's the ASCII 'DEL' character with a value of 127. - let control_char_test1 = _mm_cmpeq_epi8(chunk, _mm_set1_epi8(127)); - let control_char_mask1 = _mm_movemask_epi8(control_char_test1); - - let control_char_mask = control_char_mask0 | control_char_mask1; - - if control_char_mask != 0 { - // Check for newlines in the chunk - let newlines_test = _mm_cmpeq_epi8(chunk, _mm_set1_epi8(b'\n' as i8)); - let newlines_mask = _mm_movemask_epi8(newlines_test); - - if control_char_mask == newlines_mask { - // All control characters are newlines, record them - let mut newlines_mask = 0xFFFF0000 | newlines_mask as u32; - let output_offset = output_offset + - BytePos::from_usize(chunk_index * CHUNK_SIZE + 1); - - loop { - let index = newlines_mask.trailing_zeros(); - - if index >= CHUNK_SIZE as u32 { - // We have arrived at the end of the chunk. - break - } - - lines.push(BytePos(index) + output_offset); - - // Clear the bit, so we can find the next one. - newlines_mask &= (!1) << index; - } - - // We are done for this chunk. All control characters were - // newlines and we took care of those. - continue - } else { - // Some of the control characters are not newlines, - // fall through to the slow path below. - } - } else { - // No control characters, nothing to record for this chunk - continue - } - } - - // The slow path. - // There are control chars in here, fallback to generic decoding. - let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset; - intra_chunk_offset = analyze_source_file_generic( - &src[scan_start .. ], - CHUNK_SIZE - intra_chunk_offset, - BytePos::from_usize(scan_start) + output_offset, - lines, - multi_byte_chars, - non_narrow_chars - ); - } - - // There might still be a tail left to analyze - let tail_start = chunk_count * CHUNK_SIZE + intra_chunk_offset; - if tail_start < src.len() { - analyze_source_file_generic(&src[tail_start as usize ..], - src.len() - tail_start, - output_offset + BytePos::from_usize(tail_start), - lines, - multi_byte_chars, - non_narrow_chars); - } - } - } else { - - // The target (or compiler version) does not support SSE2 ... - fn analyze_source_file_dispatch(src: &str, - source_file_start_pos: BytePos, - lines: &mut Vec<BytePos>, - multi_byte_chars: &mut Vec<MultiByteChar>, - non_narrow_chars: &mut Vec<NonNarrowChar>) { - analyze_source_file_generic(src, - src.len(), - source_file_start_pos, - lines, - multi_byte_chars, - non_narrow_chars); - } - } -} - -// `scan_len` determines the number of bytes in `src` to scan. Note that the -// function can read past `scan_len` if a multi-byte character start within the -// range but extends past it. The overflow is returned by the function. -fn analyze_source_file_generic( - src: &str, - scan_len: usize, - output_offset: BytePos, - lines: &mut Vec<BytePos>, - multi_byte_chars: &mut Vec<MultiByteChar>, - non_narrow_chars: &mut Vec<NonNarrowChar>, -) -> usize { - assert!(src.len() >= scan_len); - let mut i = 0; - let src_bytes = src.as_bytes(); - - while i < scan_len { - let byte = unsafe { - // We verified that i < scan_len <= src.len() - *src_bytes.get_unchecked(i as usize) - }; - - // How much to advance in order to get to the next UTF-8 char in the - // string. - let mut char_len = 1; - - if byte < 32 { - // This is an ASCII control character, it could be one of the cases - // that are interesting to us. - - let pos = BytePos::from_usize(i) + output_offset; - - match byte { - b'\n' => { - lines.push(pos + BytePos(1)); - } - b'\t' => { - non_narrow_chars.push(NonNarrowChar::Tab(pos)); - } - _ => { - non_narrow_chars.push(NonNarrowChar::ZeroWidth(pos)); - } - } - } else if byte >= 127 { - // The slow path: - // This is either ASCII control character "DEL" or the beginning of - // a multibyte char. Just decode to `char`. - let c = (&src[i..]).chars().next().unwrap(); - char_len = c.len_utf8(); - - let pos = BytePos::from_usize(i) + output_offset; - - if char_len > 1 { - assert!(char_len >= 2 && char_len <= 4); - let mbc = MultiByteChar { pos, bytes: char_len as u8 }; - multi_byte_chars.push(mbc); - } - - // Assume control characters are zero width. - // FIXME: How can we decide between `width` and `width_cjk`? - let char_width = UnicodeWidthChar::width(c).unwrap_or(0); - - if char_width != 1 { - non_narrow_chars.push(NonNarrowChar::new(pos, char_width)); - } - } - - i += char_len; - } - - i - scan_len -} diff --git a/src/libsyntax_pos/analyze_source_file/tests.rs b/src/libsyntax_pos/analyze_source_file/tests.rs deleted file mode 100644 index cb418a4bdaf..00000000000 --- a/src/libsyntax_pos/analyze_source_file/tests.rs +++ /dev/null @@ -1,142 +0,0 @@ -use super::*; - -macro_rules! test { - (case: $test_name:ident, - text: $text:expr, - source_file_start_pos: $source_file_start_pos:expr, - lines: $lines:expr, - multi_byte_chars: $multi_byte_chars:expr, - non_narrow_chars: $non_narrow_chars:expr,) => { - #[test] - fn $test_name() { - let (lines, multi_byte_chars, non_narrow_chars) = - analyze_source_file($text, BytePos($source_file_start_pos)); - - let expected_lines: Vec<BytePos> = $lines.into_iter().map(|pos| BytePos(pos)).collect(); - - assert_eq!(lines, expected_lines); - - let expected_mbcs: Vec<MultiByteChar> = $multi_byte_chars - .into_iter() - .map(|(pos, bytes)| MultiByteChar { pos: BytePos(pos), bytes }) - .collect(); - - assert_eq!(multi_byte_chars, expected_mbcs); - - let expected_nncs: Vec<NonNarrowChar> = $non_narrow_chars - .into_iter() - .map(|(pos, width)| NonNarrowChar::new(BytePos(pos), width)) - .collect(); - - assert_eq!(non_narrow_chars, expected_nncs); - } - }; -} - -test!( - case: empty_text, - text: "", - source_file_start_pos: 0, - lines: vec![], - multi_byte_chars: vec![], - non_narrow_chars: vec![], -); - -test!( - case: newlines_short, - text: "a\nc", - source_file_start_pos: 0, - lines: vec![0, 2], - multi_byte_chars: vec![], - non_narrow_chars: vec![], -); - -test!( - case: newlines_long, - text: "012345678\nabcdef012345678\na", - source_file_start_pos: 0, - lines: vec![0, 10, 26], - multi_byte_chars: vec![], - non_narrow_chars: vec![], -); - -test!( - case: newline_and_multi_byte_char_in_same_chunk, - text: "01234β789\nbcdef0123456789abcdef", - source_file_start_pos: 0, - lines: vec![0, 11], - multi_byte_chars: vec![(5, 2)], - non_narrow_chars: vec![], -); - -test!( - case: newline_and_control_char_in_same_chunk, - text: "01234\u{07}6789\nbcdef0123456789abcdef", - source_file_start_pos: 0, - lines: vec![0, 11], - multi_byte_chars: vec![], - non_narrow_chars: vec![(5, 0)], -); - -test!( - case: multi_byte_char_short, - text: "aβc", - source_file_start_pos: 0, - lines: vec![0], - multi_byte_chars: vec![(1, 2)], - non_narrow_chars: vec![], -); - -test!( - case: multi_byte_char_long, - text: "0123456789abcΔf012345β", - source_file_start_pos: 0, - lines: vec![0], - multi_byte_chars: vec![(13, 2), (22, 2)], - non_narrow_chars: vec![], -); - -test!( - case: multi_byte_char_across_chunk_boundary, - text: "0123456789abcdeΔ123456789abcdef01234", - source_file_start_pos: 0, - lines: vec![0], - multi_byte_chars: vec![(15, 2)], - non_narrow_chars: vec![], -); - -test!( - case: multi_byte_char_across_chunk_boundary_tail, - text: "0123456789abcdeΔ....", - source_file_start_pos: 0, - lines: vec![0], - multi_byte_chars: vec![(15, 2)], - non_narrow_chars: vec![], -); - -test!( - case: non_narrow_short, - text: "0\t2", - source_file_start_pos: 0, - lines: vec![0], - multi_byte_chars: vec![], - non_narrow_chars: vec![(1, 4)], -); - -test!( - case: non_narrow_long, - text: "01\t3456789abcdef01234567\u{07}9", - source_file_start_pos: 0, - lines: vec![0], - multi_byte_chars: vec![], - non_narrow_chars: vec![(2, 4), (24, 0)], -); - -test!( - case: output_offset_all, - text: "01\t345\n789abcΔf01234567\u{07}9\nbcΔf", - source_file_start_pos: 1000, - lines: vec![0 + 1000, 7 + 1000, 27 + 1000], - multi_byte_chars: vec![(13 + 1000, 2), (29 + 1000, 2)], - non_narrow_chars: vec![(2 + 1000, 4), (24 + 1000, 0)], -); diff --git a/src/libsyntax_pos/caching_source_map_view.rs b/src/libsyntax_pos/caching_source_map_view.rs deleted file mode 100644 index c329f2225b0..00000000000 --- a/src/libsyntax_pos/caching_source_map_view.rs +++ /dev/null @@ -1,108 +0,0 @@ -use crate::source_map::SourceMap; -use crate::{BytePos, SourceFile}; -use rustc_data_structures::sync::Lrc; - -#[derive(Clone)] -struct CacheEntry { - time_stamp: usize, - line_number: usize, - line_start: BytePos, - line_end: BytePos, - file: Lrc<SourceFile>, - file_index: usize, -} - -#[derive(Clone)] -pub struct CachingSourceMapView<'cm> { - source_map: &'cm SourceMap, - line_cache: [CacheEntry; 3], - time_stamp: usize, -} - -impl<'cm> CachingSourceMapView<'cm> { - pub fn new(source_map: &'cm SourceMap) -> CachingSourceMapView<'cm> { - let files = source_map.files(); - let first_file = files[0].clone(); - let entry = CacheEntry { - time_stamp: 0, - line_number: 0, - line_start: BytePos(0), - line_end: BytePos(0), - file: first_file, - file_index: 0, - }; - - CachingSourceMapView { - source_map, - line_cache: [entry.clone(), entry.clone(), entry], - time_stamp: 0, - } - } - - pub fn byte_pos_to_line_and_col( - &mut self, - pos: BytePos, - ) -> Option<(Lrc<SourceFile>, usize, BytePos)> { - self.time_stamp += 1; - - // Check if the position is in one of the cached lines - for cache_entry in self.line_cache.iter_mut() { - if pos >= cache_entry.line_start && pos < cache_entry.line_end { - cache_entry.time_stamp = self.time_stamp; - - return Some(( - cache_entry.file.clone(), - cache_entry.line_number, - pos - cache_entry.line_start, - )); - } - } - - // No cache hit ... - let mut oldest = 0; - for index in 1..self.line_cache.len() { - if self.line_cache[index].time_stamp < self.line_cache[oldest].time_stamp { - oldest = index; - } - } - - let cache_entry = &mut self.line_cache[oldest]; - - // If the entry doesn't point to the correct file, fix it up - if pos < cache_entry.file.start_pos || pos >= cache_entry.file.end_pos { - let file_valid; - if self.source_map.files().len() > 0 { - let file_index = self.source_map.lookup_source_file_idx(pos); - let file = self.source_map.files()[file_index].clone(); - - if pos >= file.start_pos && pos < file.end_pos { - cache_entry.file = file; - cache_entry.file_index = file_index; - file_valid = true; - } else { - file_valid = false; - } - } else { - file_valid = false; - } - - if !file_valid { - return None; - } - } - - let line_index = cache_entry.file.lookup_line(pos).unwrap(); - let line_bounds = cache_entry.file.line_bounds(line_index); - - cache_entry.line_number = line_index + 1; - cache_entry.line_start = line_bounds.0; - cache_entry.line_end = line_bounds.1; - cache_entry.time_stamp = self.time_stamp; - - return Some(( - cache_entry.file.clone(), - cache_entry.line_number, - pos - cache_entry.line_start, - )); - } -} diff --git a/src/libsyntax_pos/edition.rs b/src/libsyntax_pos/edition.rs deleted file mode 100644 index 3017191563b..00000000000 --- a/src/libsyntax_pos/edition.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::symbol::{sym, Symbol}; -use std::fmt; -use std::str::FromStr; - -use rustc_macros::HashStable_Generic; - -/// The edition of the compiler (RFC 2052) -#[derive( - Clone, - Copy, - Hash, - PartialEq, - PartialOrd, - Debug, - RustcEncodable, - RustcDecodable, - Eq, - HashStable_Generic -)] -pub enum Edition { - // editions must be kept in order, oldest to newest - /// The 2015 edition - Edition2015, - /// The 2018 edition - Edition2018, - // when adding new editions, be sure to update: - // - // - Update the `ALL_EDITIONS` const - // - Update the EDITION_NAME_LIST const - // - add a `rust_####()` function to the session - // - update the enum in Cargo's sources as well -} - -// must be in order from oldest to newest -pub const ALL_EDITIONS: &[Edition] = &[Edition::Edition2015, Edition::Edition2018]; - -pub const EDITION_NAME_LIST: &str = "2015|2018"; - -pub const DEFAULT_EDITION: Edition = Edition::Edition2015; - -impl fmt::Display for Edition { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match *self { - Edition::Edition2015 => "2015", - Edition::Edition2018 => "2018", - }; - write!(f, "{}", s) - } -} - -impl Edition { - pub fn lint_name(&self) -> &'static str { - match *self { - Edition::Edition2015 => "rust_2015_compatibility", - Edition::Edition2018 => "rust_2018_compatibility", - } - } - - pub fn feature_name(&self) -> Symbol { - match *self { - Edition::Edition2015 => sym::rust_2015_preview, - Edition::Edition2018 => sym::rust_2018_preview, - } - } - - pub fn is_stable(&self) -> bool { - match *self { - Edition::Edition2015 => true, - Edition::Edition2018 => true, - } - } -} - -impl FromStr for Edition { - type Err = (); - fn from_str(s: &str) -> Result<Self, ()> { - match s { - "2015" => Ok(Edition::Edition2015), - "2018" => Ok(Edition::Edition2018), - _ => Err(()), - } - } -} diff --git a/src/libsyntax_pos/fatal_error.rs b/src/libsyntax_pos/fatal_error.rs deleted file mode 100644 index 718c0ddbc63..00000000000 --- a/src/libsyntax_pos/fatal_error.rs +++ /dev/null @@ -1,26 +0,0 @@ -/// 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; - -pub struct FatalErrorMarker; - -// Don't implement Send on FatalError. This makes it impossible to panic!(FatalError). -// We don't want to invoke the panic handler and print a backtrace for fatal errors. -impl !Send for FatalError {} - -impl FatalError { - pub fn raise(self) -> ! { - std::panic::resume_unwind(Box::new(FatalErrorMarker)) - } -} - -impl std::fmt::Display for FatalError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "parser fatal error") - } -} - -impl std::error::Error for FatalError {} diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs deleted file mode 100644 index fd1f07c743b..00000000000 --- a/src/libsyntax_pos/hygiene.rs +++ /dev/null @@ -1,850 +0,0 @@ -//! Machinery for hygienic macros, inspired by the `MTWT[1]` paper. -//! -//! `[1]` Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler. 2012. -//! *Macros that work together: Compile-time bindings, partial expansion, -//! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216. -//! DOI=10.1017/S0956796812000093 <https://doi.org/10.1017/S0956796812000093> - -// Hygiene data is stored in a global variable and accessed via TLS, which -// means that accesses are somewhat expensive. (`HygieneData::with` -// encapsulates a single access.) Therefore, on hot code paths it is worth -// ensuring that multiple HygieneData accesses are combined into a single -// `HygieneData::with`. -// -// This explains why `HygieneData`, `SyntaxContext` and `ExpnId` have interfaces -// with a certain amount of redundancy in them. For example, -// `SyntaxContext::outer_expn_data` combines `SyntaxContext::outer` and -// `ExpnId::expn_data` so that two `HygieneData` accesses can be performed within -// a single `HygieneData::with` call. -// -// It also explains why many functions appear in `HygieneData` and again in -// `SyntaxContext` or `ExpnId`. For example, `HygieneData::outer` and -// `SyntaxContext::outer` do the same thing, but the former is for use within a -// `HygieneData::with` call while the latter is for use outside such a call. -// When modifying this file it is important to understand this distinction, -// because getting it wrong can lead to nested `HygieneData::with` calls that -// trigger runtime aborts. (Fortunately these are obvious and easy to fix.) - -use crate::edition::Edition; -use crate::symbol::{kw, sym, Symbol}; -use crate::GLOBALS; -use crate::{Span, DUMMY_SP}; - -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; -use rustc_macros::HashStable_Generic; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use std::fmt; - -/// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct SyntaxContext(u32); - -#[derive(Debug)] -struct SyntaxContextData { - outer_expn: ExpnId, - outer_transparency: Transparency, - parent: SyntaxContext, - /// This context, but with all transparent and semi-transparent expansions filtered away. - opaque: SyntaxContext, - /// This context, but with all transparent expansions filtered away. - opaque_and_semitransparent: SyntaxContext, - /// Name of the crate to which `$crate` with this context would resolve. - dollar_crate_name: Symbol, -} - -/// A unique ID associated with a macro invocation and expansion. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub struct ExpnId(u32); - -/// A property of a macro expansion that determines how identifiers -/// produced by that expansion are resolved. -#[derive( - Copy, - Clone, - PartialEq, - Eq, - PartialOrd, - Hash, - Debug, - RustcEncodable, - RustcDecodable, - HashStable_Generic -)] -pub enum Transparency { - /// Identifier produced by a transparent expansion is always resolved at call-site. - /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this. - Transparent, - /// Identifier produced by a semi-transparent expansion may be resolved - /// either at call-site or at definition-site. - /// If it's a local variable, label or `$crate` then it's resolved at def-site. - /// Otherwise it's resolved at call-site. - /// `macro_rules` macros behave like this, built-in macros currently behave like this too, - /// but that's an implementation detail. - SemiTransparent, - /// Identifier produced by an opaque expansion is always resolved at definition-site. - /// Def-site spans in procedural macros, identifiers from `macro` by default use this. - Opaque, -} - -impl ExpnId { - pub fn fresh(expn_data: Option<ExpnData>) -> Self { - HygieneData::with(|data| data.fresh_expn(expn_data)) - } - - /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. - #[inline] - pub fn root() -> Self { - ExpnId(0) - } - - #[inline] - pub fn as_u32(self) -> u32 { - self.0 - } - - #[inline] - pub fn from_u32(raw: u32) -> ExpnId { - ExpnId(raw) - } - - #[inline] - pub fn expn_data(self) -> ExpnData { - HygieneData::with(|data| data.expn_data(self).clone()) - } - - #[inline] - pub fn set_expn_data(self, expn_data: ExpnData) { - HygieneData::with(|data| { - let old_expn_data = &mut data.expn_data[self.0 as usize]; - assert!(old_expn_data.is_none(), "expansion data is reset for an expansion ID"); - *old_expn_data = Some(expn_data); - }) - } - - pub fn is_descendant_of(self, ancestor: ExpnId) -> bool { - HygieneData::with(|data| data.is_descendant_of(self, ancestor)) - } - - /// `expn_id.outer_expn_is_descendant_of(ctxt)` is equivalent to but faster than - /// `expn_id.is_descendant_of(ctxt.outer_expn())`. - pub fn outer_expn_is_descendant_of(self, ctxt: SyntaxContext) -> bool { - HygieneData::with(|data| data.is_descendant_of(self, data.outer_expn(ctxt))) - } - - /// Returns span for the macro which originally caused this expansion to happen. - /// - /// Stops backtracing at include! boundary. - pub fn expansion_cause(mut self) -> Option<Span> { - let mut last_macro = None; - loop { - let expn_data = self.expn_data(); - // Stop going up the backtrace once include! is encountered - if expn_data.is_root() || expn_data.kind.descr() == sym::include { - break; - } - self = expn_data.call_site.ctxt().outer_expn(); - last_macro = Some(expn_data.call_site); - } - last_macro - } -} - -#[derive(Debug)] -crate struct HygieneData { - /// Each expansion should have an associated expansion data, but sometimes there's a delay - /// between creation of an expansion ID and obtaining its data (e.g. macros are collected - /// first and then resolved later), so we use an `Option` here. - expn_data: Vec<Option<ExpnData>>, - syntax_context_data: Vec<SyntaxContextData>, - syntax_context_map: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>, -} - -impl HygieneData { - crate fn new(edition: Edition) -> Self { - HygieneData { - expn_data: vec![Some(ExpnData::default(ExpnKind::Root, DUMMY_SP, edition))], - syntax_context_data: vec![SyntaxContextData { - outer_expn: ExpnId::root(), - outer_transparency: Transparency::Opaque, - parent: SyntaxContext(0), - opaque: SyntaxContext(0), - opaque_and_semitransparent: SyntaxContext(0), - dollar_crate_name: kw::DollarCrate, - }], - syntax_context_map: FxHashMap::default(), - } - } - - fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T { - GLOBALS.with(|globals| f(&mut *globals.hygiene_data.borrow_mut())) - } - - fn fresh_expn(&mut self, expn_data: Option<ExpnData>) -> ExpnId { - self.expn_data.push(expn_data); - ExpnId(self.expn_data.len() as u32 - 1) - } - - fn expn_data(&self, expn_id: ExpnId) -> &ExpnData { - self.expn_data[expn_id.0 as usize].as_ref().expect("no expansion data for an expansion ID") - } - - fn is_descendant_of(&self, mut expn_id: ExpnId, ancestor: ExpnId) -> bool { - while expn_id != ancestor { - if expn_id == ExpnId::root() { - return false; - } - expn_id = self.expn_data(expn_id).parent; - } - true - } - - fn modern(&self, ctxt: SyntaxContext) -> SyntaxContext { - self.syntax_context_data[ctxt.0 as usize].opaque - } - - fn modern_and_legacy(&self, ctxt: SyntaxContext) -> SyntaxContext { - self.syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent - } - - fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { - self.syntax_context_data[ctxt.0 as usize].outer_expn - } - - fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) { - let data = &self.syntax_context_data[ctxt.0 as usize]; - (data.outer_expn, data.outer_transparency) - } - - fn parent_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext { - self.syntax_context_data[ctxt.0 as usize].parent - } - - fn remove_mark(&self, ctxt: &mut SyntaxContext) -> (ExpnId, Transparency) { - let outer_mark = self.outer_mark(*ctxt); - *ctxt = self.parent_ctxt(*ctxt); - outer_mark - } - - fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(ExpnId, Transparency)> { - let mut marks = Vec::new(); - while ctxt != SyntaxContext::root() { - marks.push(self.outer_mark(ctxt)); - ctxt = self.parent_ctxt(ctxt); - } - marks.reverse(); - marks - } - - fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span { - while span.from_expansion() && span.ctxt() != to { - span = self.expn_data(self.outer_expn(span.ctxt())).call_site; - } - span - } - - fn adjust(&self, ctxt: &mut SyntaxContext, expn_id: ExpnId) -> Option<ExpnId> { - let mut scope = None; - while !self.is_descendant_of(expn_id, self.outer_expn(*ctxt)) { - scope = Some(self.remove_mark(ctxt).0); - } - scope - } - - fn apply_mark( - &mut self, - ctxt: SyntaxContext, - expn_id: ExpnId, - transparency: Transparency, - ) -> SyntaxContext { - assert_ne!(expn_id, ExpnId::root()); - if transparency == Transparency::Opaque { - return self.apply_mark_internal(ctxt, expn_id, transparency); - } - - let call_site_ctxt = self.expn_data(expn_id).call_site.ctxt(); - let mut call_site_ctxt = if transparency == Transparency::SemiTransparent { - self.modern(call_site_ctxt) - } else { - self.modern_and_legacy(call_site_ctxt) - }; - - if call_site_ctxt == SyntaxContext::root() { - return self.apply_mark_internal(ctxt, expn_id, transparency); - } - - // Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a - // macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition. - // - // In this case, the tokens from the macros 1.0 definition inherit the hygiene - // at their invocation. That is, we pretend that the macros 1.0 definition - // was defined at its invocation (i.e., inside the macros 2.0 definition) - // so that the macros 2.0 definition remains hygienic. - // - // See the example at `test/ui/hygiene/legacy_interaction.rs`. - for (expn_id, transparency) in self.marks(ctxt) { - call_site_ctxt = self.apply_mark_internal(call_site_ctxt, expn_id, transparency); - } - self.apply_mark_internal(call_site_ctxt, expn_id, transparency) - } - - fn apply_mark_internal( - &mut self, - ctxt: SyntaxContext, - expn_id: ExpnId, - transparency: Transparency, - ) -> SyntaxContext { - let syntax_context_data = &mut self.syntax_context_data; - let mut opaque = syntax_context_data[ctxt.0 as usize].opaque; - let mut opaque_and_semitransparent = - syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent; - - if transparency >= Transparency::Opaque { - let parent = opaque; - opaque = *self - .syntax_context_map - .entry((parent, expn_id, transparency)) - .or_insert_with(|| { - let new_opaque = SyntaxContext(syntax_context_data.len() as u32); - syntax_context_data.push(SyntaxContextData { - outer_expn: expn_id, - outer_transparency: transparency, - parent, - opaque: new_opaque, - opaque_and_semitransparent: new_opaque, - dollar_crate_name: kw::DollarCrate, - }); - new_opaque - }); - } - - if transparency >= Transparency::SemiTransparent { - let parent = opaque_and_semitransparent; - opaque_and_semitransparent = *self - .syntax_context_map - .entry((parent, expn_id, transparency)) - .or_insert_with(|| { - let new_opaque_and_semitransparent = - SyntaxContext(syntax_context_data.len() as u32); - syntax_context_data.push(SyntaxContextData { - outer_expn: expn_id, - outer_transparency: transparency, - parent, - opaque, - opaque_and_semitransparent: new_opaque_and_semitransparent, - dollar_crate_name: kw::DollarCrate, - }); - new_opaque_and_semitransparent - }); - } - - let parent = ctxt; - *self.syntax_context_map.entry((parent, expn_id, transparency)).or_insert_with(|| { - let new_opaque_and_semitransparent_and_transparent = - SyntaxContext(syntax_context_data.len() as u32); - syntax_context_data.push(SyntaxContextData { - outer_expn: expn_id, - outer_transparency: transparency, - parent, - opaque, - opaque_and_semitransparent, - dollar_crate_name: kw::DollarCrate, - }); - new_opaque_and_semitransparent_and_transparent - }) - } -} - -pub fn clear_syntax_context_map() { - HygieneData::with(|data| data.syntax_context_map = FxHashMap::default()); -} - -pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { - HygieneData::with(|data| data.walk_chain(span, to)) -} - -pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { - // The new contexts that need updating are at the end of the list and have `$crate` as a name. - let (len, to_update) = HygieneData::with(|data| { - ( - data.syntax_context_data.len(), - data.syntax_context_data - .iter() - .rev() - .take_while(|scdata| scdata.dollar_crate_name == kw::DollarCrate) - .count(), - ) - }); - // The callback must be called from outside of the `HygieneData` lock, - // since it will try to acquire it too. - let range_to_update = len - to_update..len; - let names: Vec<_> = - range_to_update.clone().map(|idx| get_name(SyntaxContext::from_u32(idx as u32))).collect(); - HygieneData::with(|data| { - range_to_update.zip(names.into_iter()).for_each(|(idx, name)| { - data.syntax_context_data[idx].dollar_crate_name = name; - }) - }) -} - -pub fn debug_hygiene_data(verbose: bool) -> String { - HygieneData::with(|data| { - if verbose { - format!("{:#?}", data) - } else { - let mut s = String::from(""); - s.push_str("Expansions:"); - data.expn_data.iter().enumerate().for_each(|(id, expn_info)| { - let expn_info = expn_info.as_ref().expect("no expansion data for an expansion ID"); - s.push_str(&format!( - "\n{}: parent: {:?}, call_site_ctxt: {:?}, kind: {:?}", - id, - expn_info.parent, - expn_info.call_site.ctxt(), - expn_info.kind, - )); - }); - s.push_str("\n\nSyntaxContexts:"); - data.syntax_context_data.iter().enumerate().for_each(|(id, ctxt)| { - s.push_str(&format!( - "\n#{}: parent: {:?}, outer_mark: ({:?}, {:?})", - id, ctxt.parent, ctxt.outer_expn, ctxt.outer_transparency, - )); - }); - s - } - }) -} - -impl SyntaxContext { - #[inline] - pub const fn root() -> Self { - SyntaxContext(0) - } - - #[inline] - crate fn as_u32(self) -> u32 { - self.0 - } - - #[inline] - crate fn from_u32(raw: u32) -> SyntaxContext { - SyntaxContext(raw) - } - - /// Extend a syntax context with a given expansion and transparency. - crate fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { - HygieneData::with(|data| data.apply_mark(self, expn_id, transparency)) - } - - /// Pulls a single mark off of the syntax context. This effectively moves the - /// context up one macro definition level. That is, if we have a nested macro - /// definition as follows: - /// - /// ```rust - /// macro_rules! f { - /// macro_rules! g { - /// ... - /// } - /// } - /// ``` - /// - /// and we have a SyntaxContext that is referring to something declared by an invocation - /// of g (call it g1), calling remove_mark will result in the SyntaxContext for the - /// invocation of f that created g1. - /// Returns the mark that was removed. - pub fn remove_mark(&mut self) -> ExpnId { - HygieneData::with(|data| data.remove_mark(self).0) - } - - pub fn marks(self) -> Vec<(ExpnId, Transparency)> { - HygieneData::with(|data| data.marks(self)) - } - - /// Adjust this context for resolution in a scope created by the given expansion. - /// For example, consider the following three resolutions of `f`: - /// - /// ```rust - /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty. - /// m!(f); - /// macro m($f:ident) { - /// mod bar { - /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. - /// pub fn $f() {} // `$f`'s `SyntaxContext` is empty. - /// } - /// foo::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m` - /// //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`, - /// //| and it resolves to `::foo::f`. - /// bar::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m` - /// //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`, - /// //| and it resolves to `::bar::f`. - /// bar::$f(); // `f`'s `SyntaxContext` is empty. - /// //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`, - /// //| and it resolves to `::bar::$f`. - /// } - /// ``` - /// This returns the expansion whose definition scope we use to privacy check the resolution, - /// or `None` if we privacy check as usual (i.e., not w.r.t. a macro definition scope). - pub fn adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> { - HygieneData::with(|data| data.adjust(self, expn_id)) - } - - /// Like `SyntaxContext::adjust`, but also modernizes `self`. - pub fn modernize_and_adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> { - HygieneData::with(|data| { - *self = data.modern(*self); - data.adjust(self, expn_id) - }) - } - - /// Adjust this context for resolution in a scope created by the given expansion - /// via a glob import with the given `SyntaxContext`. - /// For example: - /// - /// ```rust - /// m!(f); - /// macro m($i:ident) { - /// mod foo { - /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. - /// pub fn $i() {} // `$i`'s `SyntaxContext` is empty. - /// } - /// n(f); - /// macro n($j:ident) { - /// use foo::*; - /// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n` - /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`. - /// $i(); // `$i`'s `SyntaxContext` has a mark from `n` - /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`. - /// $j(); // `$j`'s `SyntaxContext` has a mark from `m` - /// //^ This cannot be glob-adjusted, so this is a resolution error. - /// } - /// } - /// ``` - /// This returns `None` if the context cannot be glob-adjusted. - /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details). - pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option<Option<ExpnId>> { - HygieneData::with(|data| { - let mut scope = None; - let mut glob_ctxt = data.modern(glob_span.ctxt()); - while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { - scope = Some(data.remove_mark(&mut glob_ctxt).0); - if data.remove_mark(self).0 != scope.unwrap() { - return None; - } - } - if data.adjust(self, expn_id).is_some() { - return None; - } - Some(scope) - }) - } - - /// Undo `glob_adjust` if possible: - /// - /// ```rust - /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) { - /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope)); - /// } - /// ``` - pub fn reverse_glob_adjust( - &mut self, - expn_id: ExpnId, - glob_span: Span, - ) -> Option<Option<ExpnId>> { - HygieneData::with(|data| { - if data.adjust(self, expn_id).is_some() { - return None; - } - - let mut glob_ctxt = data.modern(glob_span.ctxt()); - let mut marks = Vec::new(); - while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { - marks.push(data.remove_mark(&mut glob_ctxt)); - } - - let scope = marks.last().map(|mark| mark.0); - while let Some((expn_id, transparency)) = marks.pop() { - *self = data.apply_mark(*self, expn_id, transparency); - } - Some(scope) - }) - } - - pub fn hygienic_eq(self, other: SyntaxContext, expn_id: ExpnId) -> bool { - HygieneData::with(|data| { - let mut self_modern = data.modern(self); - data.adjust(&mut self_modern, expn_id); - self_modern == data.modern(other) - }) - } - - #[inline] - pub fn modern(self) -> SyntaxContext { - HygieneData::with(|data| data.modern(self)) - } - - #[inline] - pub fn modern_and_legacy(self) -> SyntaxContext { - HygieneData::with(|data| data.modern_and_legacy(self)) - } - - #[inline] - pub fn outer_expn(self) -> ExpnId { - HygieneData::with(|data| data.outer_expn(self)) - } - - /// `ctxt.outer_expn_data()` is equivalent to but faster than - /// `ctxt.outer_expn().expn_data()`. - #[inline] - pub fn outer_expn_data(self) -> ExpnData { - HygieneData::with(|data| data.expn_data(data.outer_expn(self)).clone()) - } - - #[inline] - pub fn outer_mark_with_data(self) -> (ExpnId, Transparency, ExpnData) { - HygieneData::with(|data| { - let (expn_id, transparency) = data.outer_mark(self); - (expn_id, transparency, data.expn_data(expn_id).clone()) - }) - } - - pub fn dollar_crate_name(self) -> Symbol { - HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) - } -} - -impl fmt::Debug for SyntaxContext { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "#{}", self.0) - } -} - -impl Span { - /// Creates a fresh expansion with given properties. - /// Expansions are normally created by macros, but in some cases expansions are created for - /// other compiler-generated code to set per-span properties like allowed unstable features. - /// The returned span belongs to the created expansion and has the new properties, - /// but its location is inherited from the current span. - pub fn fresh_expansion(self, expn_data: ExpnData) -> Span { - self.fresh_expansion_with_transparency(expn_data, Transparency::Transparent) - } - - pub fn fresh_expansion_with_transparency( - self, - expn_data: ExpnData, - transparency: Transparency, - ) -> Span { - HygieneData::with(|data| { - let expn_id = data.fresh_expn(Some(expn_data)); - self.with_ctxt(data.apply_mark(SyntaxContext::root(), expn_id, transparency)) - }) - } -} - -/// A subset of properties from both macro definition and macro call available through global data. -/// Avoid using this if you have access to the original definition or call structures. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub struct ExpnData { - // --- The part unique to each expansion. - /// The kind of this expansion - macro or compiler desugaring. - pub kind: ExpnKind, - /// The expansion that produced this expansion. - #[stable_hasher(ignore)] - pub parent: ExpnId, - /// The location of the actual macro invocation or syntax sugar , e.g. - /// `let x = foo!();` or `if let Some(y) = x {}` - /// - /// This may recursively refer to other macro invocations, e.g., if - /// `foo!()` invoked `bar!()` internally, and there was an - /// expression inside `bar!`; the call_site of the expression in - /// the expansion would point to the `bar!` invocation; that - /// call_site span would have its own ExpnData, with the call_site - /// pointing to the `foo!` invocation. - pub call_site: Span, - - // --- The part specific to the macro/desugaring definition. - // --- It may be reasonable to share this part between expansions with the same definition, - // --- but such sharing is known to bring some minor inconveniences without also bringing - // --- noticeable perf improvements (PR #62898). - /// The span of the macro definition (possibly dummy). - /// This span serves only informational purpose and is not used for resolution. - pub def_site: Span, - /// List of #[unstable]/feature-gated features that the macro is allowed to use - /// internally without forcing the whole crate to opt-in - /// to them. - pub allow_internal_unstable: Option<Lrc<[Symbol]>>, - /// Whether the macro is allowed to use `unsafe` internally - /// even if the user crate has `#![forbid(unsafe_code)]`. - pub allow_internal_unsafe: bool, - /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) - /// for a given macro. - pub local_inner_macros: bool, - /// Edition of the crate in which the macro is defined. - pub edition: Edition, -} - -impl ExpnData { - /// Constructs expansion data with default properties. - pub fn default(kind: ExpnKind, call_site: Span, edition: Edition) -> ExpnData { - ExpnData { - kind, - parent: ExpnId::root(), - call_site, - def_site: DUMMY_SP, - allow_internal_unstable: None, - allow_internal_unsafe: false, - local_inner_macros: false, - edition, - } - } - - pub fn allow_unstable( - kind: ExpnKind, - call_site: Span, - edition: Edition, - allow_internal_unstable: Lrc<[Symbol]>, - ) -> ExpnData { - ExpnData { - allow_internal_unstable: Some(allow_internal_unstable), - ..ExpnData::default(kind, call_site, edition) - } - } - - #[inline] - pub fn is_root(&self) -> bool { - if let ExpnKind::Root = self.kind { true } else { false } - } -} - -/// Expansion kind. -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub enum ExpnKind { - /// No expansion, aka root expansion. Only `ExpnId::root()` has this kind. - Root, - /// Expansion produced by a macro. - Macro(MacroKind, Symbol), - /// Transform done by the compiler on the AST. - AstPass(AstPass), - /// Desugaring done by the compiler during HIR lowering. - Desugaring(DesugaringKind), -} - -impl ExpnKind { - pub fn descr(&self) -> Symbol { - match *self { - ExpnKind::Root => kw::PathRoot, - ExpnKind::Macro(_, descr) => descr, - ExpnKind::AstPass(kind) => Symbol::intern(kind.descr()), - ExpnKind::Desugaring(kind) => Symbol::intern(kind.descr()), - } - } -} - -/// The kind of macro invocation or definition. -#[derive( - Clone, - Copy, - PartialEq, - Eq, - RustcEncodable, - RustcDecodable, - Hash, - Debug, - HashStable_Generic -)] -pub enum MacroKind { - /// A bang macro `foo!()`. - Bang, - /// An attribute macro `#[foo]`. - Attr, - /// A derive macro `#[derive(Foo)]` - Derive, -} - -impl MacroKind { - pub fn descr(self) -> &'static str { - match self { - MacroKind::Bang => "macro", - MacroKind::Attr => "attribute macro", - MacroKind::Derive => "derive macro", - } - } - - pub fn descr_expected(self) -> &'static str { - match self { - MacroKind::Attr => "attribute", - _ => self.descr(), - } - } - - pub fn article(self) -> &'static str { - match self { - MacroKind::Attr => "an", - _ => "a", - } - } -} - -/// The kind of AST transform. -#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub enum AstPass { - StdImports, - TestHarness, - ProcMacroHarness, -} - -impl AstPass { - fn descr(self) -> &'static str { - match self { - AstPass::StdImports => "standard library imports", - AstPass::TestHarness => "test harness", - AstPass::ProcMacroHarness => "proc macro harness", - } - } -} - -/// The kind of compiler desugaring. -#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] -pub enum DesugaringKind { - /// We desugar `if c { i } else { e }` to `match $ExprKind::Use(c) { true => i, _ => e }`. - /// However, we do not want to blame `c` for unreachability but rather say that `i` - /// is unreachable. This desugaring kind allows us to avoid blaming `c`. - /// This also applies to `while` loops. - CondTemporary, - QuestionMark, - TryBlock, - /// Desugaring of an `impl Trait` in return type position - /// to an `type Foo = impl Trait;` and replacing the - /// `impl Trait` with `Foo`. - OpaqueTy, - Async, - Await, - ForLoop, -} - -impl DesugaringKind { - /// The description wording should combine well with "desugaring of {}". - fn descr(self) -> &'static str { - match self { - DesugaringKind::CondTemporary => "`if` or `while` condition", - DesugaringKind::Async => "`async` block or function", - DesugaringKind::Await => "`await` expression", - DesugaringKind::QuestionMark => "operator `?`", - DesugaringKind::TryBlock => "`try` block", - DesugaringKind::OpaqueTy => "`impl Trait`", - DesugaringKind::ForLoop => "`for` loop", - } - } -} - -impl Encodable for ExpnId { - fn encode<E: Encoder>(&self, _: &mut E) -> Result<(), E::Error> { - Ok(()) // FIXME(jseyfried) intercrate hygiene - } -} - -impl Decodable for ExpnId { - fn decode<D: Decoder>(_: &mut D) -> Result<Self, D::Error> { - Ok(ExpnId::root()) // FIXME(jseyfried) intercrate hygiene - } -} diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs deleted file mode 100644 index a58c12f2350..00000000000 --- a/src/libsyntax_pos/lib.rs +++ /dev/null @@ -1,1672 +0,0 @@ -//! The source positions and related helper functions. -//! -//! ## Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(const_fn)] -#![feature(crate_visibility_modifier)] -#![feature(nll)] -#![feature(optin_builtin_traits)] -#![feature(rustc_attrs)] -#![feature(specialization)] -#![feature(step_trait)] - -use rustc_data_structures::AtomicRef; -use rustc_macros::HashStable_Generic; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; - -mod caching_source_map_view; -pub mod source_map; -pub use self::caching_source_map_view::CachingSourceMapView; - -pub mod edition; -use edition::Edition; -pub mod hygiene; -use hygiene::Transparency; -pub use hygiene::{DesugaringKind, ExpnData, ExpnId, ExpnKind, MacroKind, SyntaxContext}; - -mod span_encoding; -pub use span_encoding::{Span, DUMMY_SP}; - -pub mod symbol; -pub use symbol::{sym, Symbol}; - -mod analyze_source_file; -pub mod fatal_error; - -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{Lock, Lrc}; - -use std::borrow::Cow; -use std::cell::RefCell; -use std::cmp::{self, Ordering}; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::ops::{Add, Sub}; -use std::path::PathBuf; - -#[cfg(test)] -mod tests; - -pub struct Globals { - symbol_interner: Lock<symbol::Interner>, - span_interner: Lock<span_encoding::SpanInterner>, - hygiene_data: Lock<hygiene::HygieneData>, -} - -impl Globals { - pub fn new(edition: Edition) -> Globals { - Globals { - symbol_interner: Lock::new(symbol::Interner::fresh()), - span_interner: Lock::new(span_encoding::SpanInterner::default()), - hygiene_data: Lock::new(hygiene::HygieneData::new(edition)), - } - } -} - -scoped_tls::scoped_thread_local!(pub static GLOBALS: Globals); - -/// Differentiates between real files and common virtual files. -#[derive( - Debug, - Eq, - PartialEq, - Clone, - Ord, - PartialOrd, - Hash, - RustcDecodable, - RustcEncodable, - HashStable_Generic -)] -pub enum FileName { - Real(PathBuf), - /// A macro. This includes the full name of the macro, so that there are no clashes. - Macros(String), - /// Call to `quote!`. - QuoteExpansion(u64), - /// Command line. - Anon(u64), - /// Hack in `src/libsyntax/parse.rs`. - // FIXME(jseyfried) - MacroExpansion(u64), - ProcMacroSourceCode(u64), - /// Strings provided as `--cfg [cfgspec]` stored in a `crate_cfg`. - CfgSpec(u64), - /// Strings provided as crate attributes in the CLI. - CliCrateAttr(u64), - /// Custom sources for explicit parser calls from plugins and drivers. - Custom(String), - DocTest(PathBuf, isize), -} - -impl std::fmt::Display for FileName { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use FileName::*; - match *self { - Real(ref path) => write!(fmt, "{}", path.display()), - Macros(ref name) => write!(fmt, "<{} macros>", name), - QuoteExpansion(_) => write!(fmt, "<quote expansion>"), - MacroExpansion(_) => write!(fmt, "<macro expansion>"), - Anon(_) => write!(fmt, "<anon>"), - ProcMacroSourceCode(_) => write!(fmt, "<proc-macro source code>"), - CfgSpec(_) => write!(fmt, "<cfgspec>"), - CliCrateAttr(_) => write!(fmt, "<crate attribute>"), - Custom(ref s) => write!(fmt, "<{}>", s), - DocTest(ref path, _) => write!(fmt, "{}", path.display()), - } - } -} - -impl From<PathBuf> for FileName { - fn from(p: PathBuf) -> Self { - assert!(!p.to_string_lossy().ends_with('>')); - FileName::Real(p) - } -} - -impl FileName { - pub fn is_real(&self) -> bool { - use FileName::*; - match *self { - Real(_) => true, - Macros(_) - | Anon(_) - | MacroExpansion(_) - | ProcMacroSourceCode(_) - | CfgSpec(_) - | CliCrateAttr(_) - | Custom(_) - | QuoteExpansion(_) - | DocTest(_, _) => false, - } - } - - pub fn is_macros(&self) -> bool { - use FileName::*; - match *self { - Real(_) - | Anon(_) - | MacroExpansion(_) - | ProcMacroSourceCode(_) - | CfgSpec(_) - | CliCrateAttr(_) - | Custom(_) - | QuoteExpansion(_) - | DocTest(_, _) => false, - Macros(_) => true, - } - } - - pub fn quote_expansion_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::QuoteExpansion(hasher.finish()) - } - - pub fn macro_expansion_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::MacroExpansion(hasher.finish()) - } - - pub fn anon_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::Anon(hasher.finish()) - } - - pub fn proc_macro_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::ProcMacroSourceCode(hasher.finish()) - } - - pub fn cfg_spec_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::QuoteExpansion(hasher.finish()) - } - - pub fn cli_crate_attr_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::CliCrateAttr(hasher.finish()) - } - - pub fn doc_test_source_code(path: PathBuf, line: isize) -> FileName { - FileName::DocTest(path, line) - } -} - -/// Spans represent a region of code, used for error reporting. Positions in spans -/// are *absolute* positions from the beginning of the source_map, not positions -/// relative to `SourceFile`s. Methods on the `SourceMap` 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 source_map and you cannot assume -/// that the length of the `span = hi - lo`; there may be space in the `BytePos` -/// range between files. -/// -/// `SpanData` is public because `Span` uses a thread-local interner and can't be -/// sent to other threads, but some pieces of performance infra run in a separate thread. -/// Using `Span` is generally preferred. -#[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd)] -pub struct SpanData { - 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 ctxt: SyntaxContext, -} - -impl SpanData { - #[inline] - pub fn with_lo(&self, lo: BytePos) -> Span { - Span::new(lo, self.hi, self.ctxt) - } - #[inline] - pub fn with_hi(&self, hi: BytePos) -> Span { - Span::new(self.lo, hi, self.ctxt) - } - #[inline] - pub fn with_ctxt(&self, ctxt: SyntaxContext) -> Span { - Span::new(self.lo, self.hi, ctxt) - } -} - -// The interner is pointed to by a thread local value which is only set on the main thread -// with parallelization is disabled. So we don't allow `Span` to transfer between threads -// to avoid panics and other errors, even though it would be memory safe to do so. -#[cfg(not(parallel_compiler))] -impl !Send for Span {} -#[cfg(not(parallel_compiler))] -impl !Sync for Span {} - -impl PartialOrd for Span { - fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> { - PartialOrd::partial_cmp(&self.data(), &rhs.data()) - } -} -impl Ord for Span { - fn cmp(&self, rhs: &Self) -> Ordering { - Ord::cmp(&self.data(), &rhs.data()) - } -} - -/// 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, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)] -pub struct MultiSpan { - primary_spans: Vec<Span>, - span_labels: Vec<(Span, String)>, -} - -impl Span { - #[inline] - pub fn lo(self) -> BytePos { - self.data().lo - } - #[inline] - pub fn with_lo(self, lo: BytePos) -> Span { - self.data().with_lo(lo) - } - #[inline] - pub fn hi(self) -> BytePos { - self.data().hi - } - #[inline] - pub fn with_hi(self, hi: BytePos) -> Span { - self.data().with_hi(hi) - } - #[inline] - pub fn ctxt(self) -> SyntaxContext { - self.data().ctxt - } - #[inline] - pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span { - self.data().with_ctxt(ctxt) - } - - /// Returns `true` if this is a dummy span with any hygienic context. - #[inline] - pub fn is_dummy(self) -> bool { - let span = self.data(); - span.lo.0 == 0 && span.hi.0 == 0 - } - - /// Returns `true` if this span comes from a macro or desugaring. - #[inline] - pub fn from_expansion(self) -> bool { - self.ctxt() != SyntaxContext::root() - } - - #[inline] - pub fn with_root_ctxt(lo: BytePos, hi: BytePos) -> Span { - Span::new(lo, hi, SyntaxContext::root()) - } - - /// Returns a new span representing an empty span at the beginning of this span - #[inline] - pub fn shrink_to_lo(self) -> Span { - let span = self.data(); - span.with_hi(span.lo) - } - /// Returns a new span representing an empty span at the end of this span. - #[inline] - pub fn shrink_to_hi(self) -> Span { - let span = self.data(); - span.with_lo(span.hi) - } - - /// Returns `self` if `self` is not the dummy span, and `other` otherwise. - pub fn substitute_dummy(self, other: Span) -> Span { - if self.is_dummy() { other } else { self } - } - - /// Returns `true` if `self` fully encloses `other`. - pub fn contains(self, other: Span) -> bool { - let span = self.data(); - let other = other.data(); - span.lo <= other.lo && other.hi <= span.hi - } - - /// Returns `true` if `self` touches `other`. - pub fn overlaps(self, other: Span) -> bool { - let span = self.data(); - let other = other.data(); - span.lo < other.hi && other.lo < span.hi - } - - /// Returns `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 { - let span = self.data(); - let other = other.data(); - span.lo == other.lo && span.hi == other.hi - } - - /// Returns `Some(span)`, where the start is trimmed by the end of `other`. - pub fn trim_start(self, other: Span) -> Option<Span> { - let span = self.data(); - let other = other.data(); - if span.hi > other.hi { Some(span.with_lo(cmp::max(span.lo, other.hi))) } else { None } - } - - /// Returns the source span -- this is either the supplied span, or the span for - /// the macro callsite that expanded to it. - pub fn source_callsite(self) -> Span { - let expn_data = self.ctxt().outer_expn_data(); - if !expn_data.is_root() { expn_data.call_site.source_callsite() } else { self } - } - - /// The `Span` for the tokens in the previous macro expansion from which `self` was generated, - /// if any. - pub fn parent(self) -> Option<Span> { - let expn_data = self.ctxt().outer_expn_data(); - if !expn_data.is_root() { Some(expn_data.call_site) } else { None } - } - - /// Edition of the crate from which this span came. - pub fn edition(self) -> edition::Edition { - self.ctxt().outer_expn_data().edition - } - - #[inline] - pub fn rust_2015(&self) -> bool { - self.edition() == edition::Edition::Edition2015 - } - - #[inline] - pub fn rust_2018(&self) -> bool { - self.edition() >= edition::Edition::Edition2018 - } - - /// Returns the source callee. - /// - /// Returns `None` if the supplied span has no expansion trace, - /// else returns the `ExpnData` for the macro definition - /// corresponding to the source callsite. - pub fn source_callee(self) -> Option<ExpnData> { - fn source_callee(expn_data: ExpnData) -> ExpnData { - let next_expn_data = expn_data.call_site.ctxt().outer_expn_data(); - if !next_expn_data.is_root() { source_callee(next_expn_data) } else { expn_data } - } - let expn_data = self.ctxt().outer_expn_data(); - if !expn_data.is_root() { Some(source_callee(expn_data)) } else { None } - } - - /// Checks if a span is "internal" to a macro in which `#[unstable]` - /// items can be used (that is, a macro marked with - /// `#[allow_internal_unstable]`). - pub fn allows_unstable(&self, feature: Symbol) -> bool { - self.ctxt().outer_expn_data().allow_internal_unstable.map_or(false, |features| { - features - .iter() - .any(|&f| f == feature || f == sym::allow_internal_unstable_backcompat_hack) - }) - } - - /// Checks if this span arises from a compiler desugaring of kind `kind`. - pub fn is_desugaring(&self, kind: DesugaringKind) -> bool { - match self.ctxt().outer_expn_data().kind { - ExpnKind::Desugaring(k) => k == kind, - _ => false, - } - } - - /// Returns the compiler desugaring that created this span, or `None` - /// if this span is not from a desugaring. - pub fn desugaring_kind(&self) -> Option<DesugaringKind> { - match self.ctxt().outer_expn_data().kind { - ExpnKind::Desugaring(k) => Some(k), - _ => None, - } - } - - /// Checks 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 { - self.ctxt().outer_expn_data().allow_internal_unsafe - } - - pub fn macro_backtrace(mut self) -> Vec<MacroBacktrace> { - let mut prev_span = DUMMY_SP; - let mut result = vec![]; - loop { - let expn_data = self.ctxt().outer_expn_data(); - if expn_data.is_root() { - break; - } - // Don't print recursive invocations. - if !expn_data.call_site.source_equal(&prev_span) { - let (pre, post) = match expn_data.kind { - ExpnKind::Root => break, - ExpnKind::Desugaring(..) => ("desugaring of ", ""), - ExpnKind::AstPass(..) => ("", ""), - ExpnKind::Macro(macro_kind, _) => match macro_kind { - MacroKind::Bang => ("", "!"), - MacroKind::Attr => ("#[", "]"), - MacroKind::Derive => ("#[derive(", ")]"), - }, - }; - result.push(MacroBacktrace { - call_site: expn_data.call_site, - macro_decl_name: format!("{}{}{}", pre, expn_data.kind.descr(), post), - def_site_span: expn_data.def_site, - }); - } - - prev_span = self; - self = expn_data.call_site; - } - result - } - - /// Returns a `Span` that would enclose both `self` and `end`. - pub fn to(self, end: Span) -> Span { - let span_data = self.data(); - let end_data = end.data(); - // FIXME(jseyfried): `self.ctxt` should always equal `end.ctxt` here (cf. issue #23480). - // Return the macro span on its own to avoid weird diagnostic output. It is preferable to - // have an incomplete span than a completely nonsensical one. - if span_data.ctxt != end_data.ctxt { - if span_data.ctxt == SyntaxContext::root() { - return end; - } else if end_data.ctxt == SyntaxContext::root() { - return self; - } - // Both spans fall within a macro. - // FIXME(estebank): check if it is the *same* macro. - } - Span::new( - cmp::min(span_data.lo, end_data.lo), - cmp::max(span_data.hi, end_data.hi), - if span_data.ctxt == SyntaxContext::root() { end_data.ctxt } else { span_data.ctxt }, - ) - } - - /// Returns a `Span` between the end of `self` to the beginning of `end`. - pub fn between(self, end: Span) -> Span { - let span = self.data(); - let end = end.data(); - Span::new( - span.hi, - end.lo, - if end.ctxt == SyntaxContext::root() { end.ctxt } else { span.ctxt }, - ) - } - - /// Returns a `Span` between the beginning of `self` to the beginning of `end`. - pub fn until(self, end: Span) -> Span { - let span = self.data(); - let end = end.data(); - Span::new( - span.lo, - end.lo, - if end.ctxt == SyntaxContext::root() { end.ctxt } else { span.ctxt }, - ) - } - - pub fn from_inner(self, inner: InnerSpan) -> Span { - let span = self.data(); - Span::new( - span.lo + BytePos::from_usize(inner.start), - span.lo + BytePos::from_usize(inner.end), - span.ctxt, - ) - } - - /// Equivalent of `Span::def_site` from the proc macro API, - /// except that the location is taken from the `self` span. - pub fn with_def_site_ctxt(self, expn_id: ExpnId) -> Span { - self.with_ctxt_from_mark(expn_id, Transparency::Opaque) - } - - /// Equivalent of `Span::call_site` from the proc macro API, - /// except that the location is taken from the `self` span. - pub fn with_call_site_ctxt(&self, expn_id: ExpnId) -> Span { - self.with_ctxt_from_mark(expn_id, Transparency::Transparent) - } - - /// Equivalent of `Span::mixed_site` from the proc macro API, - /// except that the location is taken from the `self` span. - pub fn with_mixed_site_ctxt(&self, expn_id: ExpnId) -> Span { - self.with_ctxt_from_mark(expn_id, Transparency::SemiTransparent) - } - - /// Produces a span with the same location as `self` and context produced by a macro with the - /// given ID and transparency, assuming that macro was defined directly and not produced by - /// some other macro (which is the case for built-in and procedural macros). - pub fn with_ctxt_from_mark(self, expn_id: ExpnId, transparency: Transparency) -> Span { - self.with_ctxt(SyntaxContext::root().apply_mark(expn_id, transparency)) - } - - #[inline] - pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> Span { - let span = self.data(); - span.with_ctxt(span.ctxt.apply_mark(expn_id, transparency)) - } - - #[inline] - pub fn remove_mark(&mut self) -> ExpnId { - let mut span = self.data(); - let mark = span.ctxt.remove_mark(); - *self = Span::new(span.lo, span.hi, span.ctxt); - mark - } - - #[inline] - pub fn adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> { - let mut span = self.data(); - let mark = span.ctxt.adjust(expn_id); - *self = Span::new(span.lo, span.hi, span.ctxt); - mark - } - - #[inline] - pub fn modernize_and_adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> { - let mut span = self.data(); - let mark = span.ctxt.modernize_and_adjust(expn_id); - *self = Span::new(span.lo, span.hi, span.ctxt); - mark - } - - #[inline] - pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option<Option<ExpnId>> { - let mut span = self.data(); - let mark = span.ctxt.glob_adjust(expn_id, glob_span); - *self = Span::new(span.lo, span.hi, span.ctxt); - mark - } - - #[inline] - pub fn reverse_glob_adjust( - &mut self, - expn_id: ExpnId, - glob_span: Span, - ) -> Option<Option<ExpnId>> { - let mut span = self.data(); - let mark = span.ctxt.reverse_glob_adjust(expn_id, glob_span); - *self = Span::new(span.lo, span.hi, span.ctxt); - mark - } - - #[inline] - pub fn modern(self) -> Span { - let span = self.data(); - span.with_ctxt(span.ctxt.modern()) - } - - #[inline] - pub fn modern_and_legacy(self) -> Span { - let span = self.data(); - span.with_ctxt(span.ctxt.modern_and_legacy()) - } -} - -#[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>, -} - -impl Default for Span { - fn default() -> Self { - DUMMY_SP - } -} - -impl rustc_serialize::UseSpecializedEncodable for Span { - fn default_encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { - let span = self.data(); - s.emit_struct("Span", 2, |s| { - s.emit_struct_field("lo", 0, |s| span.lo.encode(s))?; - - s.emit_struct_field("hi", 1, |s| span.hi.encode(s)) - }) - } -} - -impl rustc_serialize::UseSpecializedDecodable for Span { - fn default_decode<D: Decoder>(d: &mut D) -> Result<Span, D::Error> { - d.read_struct("Span", 2, |d| { - let lo = d.read_struct_field("lo", 0, Decodable::decode)?; - let hi = d.read_struct_field("hi", 1, Decodable::decode)?; - Ok(Span::with_root_ctxt(lo, hi)) - }) - } -} - -pub fn default_span_debug(span: Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Span") - .field("lo", &span.lo()) - .field("hi", &span.hi()) - .field("ctxt", &span.ctxt()) - .finish() -} - -impl fmt::Debug for Span { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*SPAN_DEBUG)(*self, f) - } -} - -impl fmt::Debug for SpanData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*SPAN_DEBUG)(Span::new(self.lo, self.hi, self.ctxt), f) - } -} - -impl MultiSpan { - #[inline] - 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 `true` if any of the primary spans are displayable. - pub fn has_primary_spans(&self) -> bool { - self.primary_spans.iter().any(|sp| !sp.is_dummy()) - } - - /// Returns `true` if this contains only a dummy primary span with any hygienic context. - pub fn is_dummy(&self) -> bool { - let mut is_dummy = true; - for span in &self.primary_spans { - if !span.is_dummy() { - is_dummy = false; - } - } - is_dummy - } - - /// Replaces all occurrences of one Span with another. Used to move `Span`s in areas that don't - /// display well (like std macros). Returns whether replacements occurred. - pub fn replace(&mut self, before: Span, after: Span) -> bool { - let mut replacements_occurred = false; - for primary_span in &mut self.primary_spans { - if *primary_span == before { - *primary_span = after; - replacements_occurred = true; - } - } - for span_label in &mut self.span_labels { - if span_label.0 == before { - span_label.0 = after; - replacements_occurred = true; - } - } - replacements_occurred - } - - /// 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 = self - .span_labels - .iter() - .map(|&(span, ref label)| SpanLabel { - span, - is_primary: is_primary(span), - label: Some(label.clone()), - }) - .collect::<Vec<_>>(); - - for &span in &self.primary_spans { - if !span_labels.iter().any(|sl| sl.span == span) { - span_labels.push(SpanLabel { span, is_primary: true, label: None }); - } - } - - span_labels - } - - /// Returns `true` if any of the span labels is displayable. - pub fn has_span_labels(&self) -> bool { - self.span_labels.iter().any(|(sp, _)| !sp.is_dummy()) - } -} - -impl From<Span> for MultiSpan { - fn from(span: Span) -> MultiSpan { - MultiSpan::from_span(span) - } -} - -impl From<Vec<Span>> for MultiSpan { - fn from(spans: Vec<Span>) -> MultiSpan { - MultiSpan::from_spans(spans) - } -} - -/// Identifies an offset of a multi-byte character in a `SourceFile`. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq, Debug)] -pub struct MultiByteChar { - /// The absolute offset of the character in the `SourceMap`. - pub pos: BytePos, - /// The number of bytes, `>= 2`. - pub bytes: u8, -} - -/// Identifies an offset of a non-narrow character in a `SourceFile`. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq, Debug)] -pub enum NonNarrowChar { - /// Represents a zero-width character. - ZeroWidth(BytePos), - /// Represents a wide (full-width) character. - Wide(BytePos), - /// Represents a tab character, represented visually with a width of 4 characters. - Tab(BytePos), -} - -impl NonNarrowChar { - fn new(pos: BytePos, width: usize) -> Self { - match width { - 0 => NonNarrowChar::ZeroWidth(pos), - 2 => NonNarrowChar::Wide(pos), - 4 => NonNarrowChar::Tab(pos), - _ => panic!("width {} given for non-narrow character", width), - } - } - - /// Returns the absolute offset of the character in the `SourceMap`. - pub fn pos(&self) -> BytePos { - match *self { - NonNarrowChar::ZeroWidth(p) | NonNarrowChar::Wide(p) | NonNarrowChar::Tab(p) => p, - } - } - - /// Returns the width of the character, 0 (zero-width) or 2 (wide). - pub fn width(&self) -> usize { - match *self { - NonNarrowChar::ZeroWidth(_) => 0, - NonNarrowChar::Wide(_) => 2, - NonNarrowChar::Tab(_) => 4, - } - } -} - -impl Add<BytePos> for NonNarrowChar { - type Output = Self; - - fn add(self, rhs: BytePos) -> Self { - match self { - NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos + rhs), - NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos + rhs), - NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos + rhs), - } - } -} - -impl Sub<BytePos> for NonNarrowChar { - type Output = Self; - - fn sub(self, rhs: BytePos) -> Self { - match self { - NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos - rhs), - NonNarrowChar::Wide(pos) => NonNarrowChar::Wide(pos - rhs), - NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos - rhs), - } - } -} - -/// Identifies an offset of a character that was normalized away from `SourceFile`. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq, Debug)] -pub struct NormalizedPos { - /// The absolute offset of the character in the `SourceMap`. - pub pos: BytePos, - /// The difference between original and normalized string at position. - pub diff: u32, -} - -/// The state of the lazy external source loading mechanism of a `SourceFile`. -#[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 `SourceFile` 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, - } - } -} - -#[derive(Debug)] -pub struct OffsetOverflowError; - -/// A single source in the `SourceMap`. -#[derive(Clone)] -pub struct SourceFile { - /// 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, - /// `true` if the `name` field above has been modified by `--remap-path-prefix`. - pub name_was_remapped: bool, - /// The unmapped path of the file that the source came from. - /// Set to `None` if the `SourceFile` was imported from an external crate. - pub unmapped_path: Option<FileName>, - /// Indicates which crate this `SourceFile` was imported from. - pub crate_of_origin: u32, - /// The complete source code. - pub src: Option<Lrc<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: Lock<ExternalSource>, - /// The start position of this source in the `SourceMap`. - pub start_pos: BytePos, - /// The end position of this source in the `SourceMap`. - pub end_pos: BytePos, - /// Locations of lines beginnings in the source code. - pub lines: Vec<BytePos>, - /// Locations of multi-byte characters in the source code. - pub multibyte_chars: Vec<MultiByteChar>, - /// Width of characters that are not narrow in the source code. - pub non_narrow_chars: Vec<NonNarrowChar>, - /// Locations of characters removed during normalization. - pub normalized_pos: Vec<NormalizedPos>, - /// A hash of the filename, used for speeding up hashing in incremental compilation. - pub name_hash: u128, -} - -impl Encodable for SourceFile { - fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("SourceFile", 8, |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", 2, |s| self.src_hash.encode(s))?; - s.emit_struct_field("start_pos", 3, |s| self.start_pos.encode(s))?; - s.emit_struct_field("end_pos", 4, |s| self.end_pos.encode(s))?; - s.emit_struct_field("lines", 5, |s| { - let lines = &self.lines[..]; - // 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", 6, |s| self.multibyte_chars.encode(s))?; - s.emit_struct_field("non_narrow_chars", 7, |s| self.non_narrow_chars.encode(s))?; - s.emit_struct_field("name_hash", 8, |s| self.name_hash.encode(s))?; - s.emit_struct_field("normalized_pos", 9, |s| self.normalized_pos.encode(s)) - }) - } -} - -impl Decodable for SourceFile { - fn decode<D: Decoder>(d: &mut D) -> Result<SourceFile, D::Error> { - d.read_struct("SourceFile", 8, |d| { - let name: FileName = 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 src_hash: u128 = d.read_struct_field("src_hash", 2, |d| Decodable::decode(d))?; - let start_pos: BytePos = - d.read_struct_field("start_pos", 3, |d| Decodable::decode(d))?; - let end_pos: BytePos = d.read_struct_field("end_pos", 4, |d| Decodable::decode(d))?; - let lines: Vec<BytePos> = d.read_struct_field("lines", 5, |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", 6, |d| Decodable::decode(d))?; - let non_narrow_chars: Vec<NonNarrowChar> = - d.read_struct_field("non_narrow_chars", 7, |d| Decodable::decode(d))?; - let name_hash: u128 = d.read_struct_field("name_hash", 8, |d| Decodable::decode(d))?; - let normalized_pos: Vec<NormalizedPos> = - d.read_struct_field("normalized_pos", 9, |d| Decodable::decode(d))?; - Ok(SourceFile { - name, - name_was_remapped, - unmapped_path: None, - // `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, - end_pos, - src: None, - src_hash, - external_src: Lock::new(ExternalSource::AbsentOk), - lines, - multibyte_chars, - non_narrow_chars, - normalized_pos, - name_hash, - }) - }) - } -} - -impl fmt::Debug for SourceFile { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "SourceFile({})", self.name) - } -} - -impl SourceFile { - pub fn new( - name: FileName, - name_was_remapped: bool, - unmapped_path: FileName, - mut src: String, - start_pos: BytePos, - ) -> Result<SourceFile, OffsetOverflowError> { - let normalized_pos = normalize_src(&mut src, start_pos); - - let src_hash = { - let mut hasher: StableHasher = StableHasher::new(); - hasher.write(src.as_bytes()); - hasher.finish::<u128>() - }; - let name_hash = { - let mut hasher: StableHasher = StableHasher::new(); - name.hash(&mut hasher); - hasher.finish::<u128>() - }; - let end_pos = start_pos.to_usize() + src.len(); - if end_pos > u32::max_value() as usize { - return Err(OffsetOverflowError); - } - - let (lines, multibyte_chars, non_narrow_chars) = - analyze_source_file::analyze_source_file(&src[..], start_pos); - - Ok(SourceFile { - name, - name_was_remapped, - unmapped_path: Some(unmapped_path), - crate_of_origin: 0, - src: Some(Lrc::new(src)), - src_hash, - external_src: Lock::new(ExternalSource::Unneeded), - start_pos, - end_pos: Pos::from_usize(end_pos), - lines, - multibyte_chars, - non_narrow_chars, - normalized_pos, - name_hash, - }) - } - - /// Returns the `BytePos` of the beginning of the current line. - pub fn line_begin_pos(&self, pos: BytePos) -> BytePos { - let line_index = self.lookup_line(pos).unwrap(); - self.lines[line_index] - } - - /// 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(); - // Check that no-one else have provided the source while we were getting it - if *external_src == ExternalSource::AbsentOk { - if let Some(src) = src { - let mut hasher: StableHasher = StableHasher::new(); - hasher.write(src.as_bytes()); - - if hasher.finish::<u128>() == self.src_hash { - *external_src = ExternalSource::Present(src); - return true; - } - } else { - *external_src = ExternalSource::AbsentErr; - } - - false - } else { - self.src.is_some() || external_src.get_source().is_some() - } - } else { - self.src.is_some() || self.external_src.borrow().get_source().is_some() - } - } - - /// Gets 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, - } - } - - let begin = { - let line = if let Some(line) = self.lines.get(line_number) { - line - } else { - return None; - }; - let begin: BytePos = *line - self.start_pos; - 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 is_real_file(&self) -> bool { - self.name.is_real() - } - - pub fn is_imported(&self) -> bool { - self.src.is_none() - } - - pub fn byte_length(&self) -> u32 { - self.end_pos.0 - self.start_pos.0 - } - pub fn count_lines(&self) -> usize { - self.lines.len() - } - - /// Finds the line containing the given position. The return value is the - /// index into the `lines` array of this `SourceFile`, not the 1-based line - /// number. If the source_file is empty or the position is located before the - /// first line, `None` is returned. - pub fn lookup_line(&self, pos: BytePos) -> Option<usize> { - if self.lines.len() == 0 { - return None; - } - - let line_index = lookup_line(&self.lines[..], pos); - assert!(line_index < self.lines.len() as isize); - if line_index >= 0 { Some(line_index as usize) } else { None } - } - - pub fn line_bounds(&self, line_index: usize) -> (BytePos, BytePos) { - if self.start_pos == self.end_pos { - return (self.start_pos, self.end_pos); - } - - assert!(line_index < self.lines.len()); - if line_index == (self.lines.len() - 1) { - (self.lines[line_index], self.end_pos) - } else { - (self.lines[line_index], self.lines[line_index + 1]) - } - } - - #[inline] - pub fn contains(&self, byte_pos: BytePos) -> bool { - byte_pos >= self.start_pos && byte_pos <= self.end_pos - } - - /// Calculates the original byte position relative to the start of the file - /// based on the given byte position. - pub fn original_relative_byte_pos(&self, pos: BytePos) -> BytePos { - // Diff before any records is 0. Otherwise use the previously recorded - // diff as that applies to the following characters until a new diff - // is recorded. - let diff = match self.normalized_pos.binary_search_by(|np| np.pos.cmp(&pos)) { - Ok(i) => self.normalized_pos[i].diff, - Err(i) if i == 0 => 0, - Err(i) => self.normalized_pos[i - 1].diff, - }; - - BytePos::from_u32(pos.0 - self.start_pos.0 + diff) - } -} - -/// Normalizes the source code and records the normalizations. -fn normalize_src(src: &mut String, start_pos: BytePos) -> Vec<NormalizedPos> { - let mut normalized_pos = vec![]; - remove_bom(src, &mut normalized_pos); - normalize_newlines(src, &mut normalized_pos); - - // Offset all the positions by start_pos to match the final file positions. - for np in &mut normalized_pos { - np.pos.0 += start_pos.0; - } - - normalized_pos -} - -/// Removes UTF-8 BOM, if any. -fn remove_bom(src: &mut String, normalized_pos: &mut Vec<NormalizedPos>) { - if src.starts_with("\u{feff}") { - src.drain(..3); - normalized_pos.push(NormalizedPos { pos: BytePos(0), diff: 3 }); - } -} - -/// Replaces `\r\n` with `\n` in-place in `src`. -/// -/// Returns error if there's a lone `\r` in the string -fn normalize_newlines(src: &mut String, normalized_pos: &mut Vec<NormalizedPos>) { - if !src.as_bytes().contains(&b'\r') { - return; - } - - // We replace `\r\n` with `\n` in-place, which doesn't break utf-8 encoding. - // While we *can* call `as_mut_vec` and do surgery on the live string - // directly, let's rather steal the contents of `src`. This makes the code - // safe even if a panic occurs. - - let mut buf = std::mem::replace(src, String::new()).into_bytes(); - let mut gap_len = 0; - let mut tail = buf.as_mut_slice(); - let mut cursor = 0; - let original_gap = normalized_pos.last().map_or(0, |l| l.diff); - loop { - let idx = match find_crlf(&tail[gap_len..]) { - None => tail.len(), - Some(idx) => idx + gap_len, - }; - tail.copy_within(gap_len..idx, 0); - tail = &mut tail[idx - gap_len..]; - if tail.len() == gap_len { - break; - } - cursor += idx - gap_len; - gap_len += 1; - normalized_pos.push(NormalizedPos { - pos: BytePos::from_usize(cursor + 1), - diff: original_gap + gap_len as u32, - }); - } - - // Account for removed `\r`. - // After `set_len`, `buf` is guaranteed to contain utf-8 again. - let new_len = buf.len() - gap_len; - unsafe { - buf.set_len(new_len); - *src = String::from_utf8_unchecked(buf); - } - - fn find_crlf(src: &[u8]) -> Option<usize> { - let mut search_idx = 0; - while let Some(idx) = find_cr(&src[search_idx..]) { - if src[search_idx..].get(idx + 1) != Some(&b'\n') { - search_idx += idx + 1; - continue; - } - return Some(search_idx + idx); - } - None - } - - fn find_cr(src: &[u8]) -> Option<usize> { - src.iter().position(|&b| b == b'\r') - } -} - -// _____________________________________________________________________________ -// Pos, BytePos, CharPos -// - -pub trait Pos { - fn from_usize(n: usize) -> Self; - fn to_usize(&self) -> usize; - fn from_u32(n: u32) -> Self; - fn to_u32(&self) -> u32; -} - -/// 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 UTF-8 characters, a byte offset -/// is not equivalent to a character offset. The `SourceMap` will convert `BytePos` -/// values to `CharPos` values as necessary. -#[derive(Copy, Clone, PartialEq, Eq, 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 { - #[inline(always)] - fn from_usize(n: usize) -> BytePos { - BytePos(n as u32) - } - - #[inline(always)] - fn to_usize(&self) -> usize { - self.0 as usize - } - - #[inline(always)] - fn from_u32(n: u32) -> BytePos { - BytePos(n) - } - - #[inline(always)] - fn to_u32(&self) -> u32 { - self.0 - } -} - -impl Add for BytePos { - type Output = BytePos; - - #[inline(always)] - fn add(self, rhs: BytePos) -> BytePos { - BytePos((self.to_usize() + rhs.to_usize()) as u32) - } -} - -impl Sub for BytePos { - type Output = BytePos; - - #[inline(always)] - 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 { - #[inline(always)] - fn from_usize(n: usize) -> CharPos { - CharPos(n) - } - - #[inline(always)] - fn to_usize(&self) -> usize { - self.0 - } - - #[inline(always)] - fn from_u32(n: u32) -> CharPos { - CharPos(n as usize) - } - - #[inline(always)] - fn to_u32(&self) -> u32 { - self.0 as u32 - } -} - -impl Add for CharPos { - type Output = CharPos; - - #[inline(always)] - fn add(self, rhs: CharPos) -> CharPos { - CharPos(self.to_usize() + rhs.to_usize()) - } -} - -impl Sub for CharPos { - type Output = CharPos; - - #[inline(always)] - fn sub(self, rhs: CharPos) -> CharPos { - CharPos(self.to_usize() - rhs.to_usize()) - } -} - -// _____________________________________________________________________________ -// Loc, SourceFileAndLine, SourceFileAndBytePos -// - -/// A source code location used for error reporting. -#[derive(Debug, Clone)] -pub struct Loc { - /// Information about the original source. - pub file: Lrc<SourceFile>, - /// The (1-based) line number. - pub line: usize, - /// The (0-based) column offset. - pub col: CharPos, - /// The (0-based) column offset when displayed. - pub col_display: usize, -} - -// Used to be structural records. -#[derive(Debug)] -pub struct SourceFileAndLine { - pub sf: Lrc<SourceFile>, - pub line: usize, -} -#[derive(Debug)] -pub struct SourceFileAndBytePos { - pub sf: Lrc<SourceFile>, - pub pos: BytePos, -} - -#[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: Lrc<SourceFile>, - pub lines: Vec<LineInfo>, -} - -pub static SPAN_DEBUG: AtomicRef<fn(Span, &mut fmt::Formatter<'_>) -> fmt::Result> = - AtomicRef::new(&(default_span_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); - -#[derive(Debug)] -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 (possibly dummy) - pub def_site_span: Span, -} - -// _____________________________________________________________________________ -// SpanLinesError, SpanSnippetError, DistinctSources, MalformedSourceMapPositions -// - -pub type FileLinesResult = Result<FileLines, SpanLinesError>; - -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum SpanLinesError { - DistinctSources(DistinctSources), -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum SpanSnippetError { - IllFormedSpan(Span), - DistinctSources(DistinctSources), - MalformedForSourcemap(MalformedSourceMapPositions), - SourceNotAvailable { filename: FileName }, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct DistinctSources { - pub begin: (FileName, BytePos), - pub end: (FileName, BytePos), -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct MalformedSourceMapPositions { - pub name: FileName, - pub source_len: usize, - pub begin_pos: BytePos, - pub end_pos: BytePos, -} - -/// Range inside of a `Span` used for diagnostics when we only have access to relative positions. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct InnerSpan { - pub start: usize, - pub end: usize, -} - -impl InnerSpan { - pub fn new(start: usize, end: usize) -> InnerSpan { - InnerSpan { start, end } - } -} - -// Given a slice of line start positions and a position, returns the index of -// the line the position is on. Returns -1 if the position is located before -// the first line. -fn lookup_line(lines: &[BytePos], pos: BytePos) -> isize { - match lines.binary_search(&pos) { - Ok(line) => line as isize, - Err(line) => line as isize - 1, - } -} - -/// Requirements for a `StableHashingContext` to be used in this crate. -/// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc. -pub trait HashStableContext { - fn hash_spans(&self) -> bool; - fn byte_pos_to_line_and_col( - &mut self, - byte: BytePos, - ) -> Option<(Lrc<SourceFile>, usize, BytePos)>; -} - -impl<CTX> HashStable<CTX> for Span -where - CTX: HashStableContext, -{ - /// Hashes a span in a stable way. We can't directly hash the span's `BytePos` - /// fields (that would be similar to hashing pointers, since those are just - /// offsets into the `SourceMap`). Instead, we hash the (file name, line, column) - /// triple, which stays the same even if the containing `SourceFile` has moved - /// within the `SourceMap`. - /// Also note that we are hashing byte offsets for the column, not unicode - /// codepoint offsets. For the purpose of the hash that's sufficient. - /// Also, hashing filenames is expensive so we avoid doing it twice when the - /// span starts and ends in the same file, which is almost always the case. - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - const TAG_VALID_SPAN: u8 = 0; - const TAG_INVALID_SPAN: u8 = 1; - const TAG_EXPANSION: u8 = 0; - const TAG_NO_EXPANSION: u8 = 1; - - if !ctx.hash_spans() { - return; - } - - if *self == DUMMY_SP { - return std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); - } - - // If this is not an empty or invalid span, we want to hash the last - // position that belongs to it, as opposed to hashing the first - // position past it. - let span = self.data(); - let (file_lo, line_lo, col_lo) = match ctx.byte_pos_to_line_and_col(span.lo) { - Some(pos) => pos, - None => { - return std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); - } - }; - - if !file_lo.contains(span.hi) { - return std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); - } - - std::hash::Hash::hash(&TAG_VALID_SPAN, hasher); - // We truncate the stable ID hash and line and column numbers. The chances - // of causing a collision this way should be minimal. - std::hash::Hash::hash(&(file_lo.name_hash as u64), hasher); - - let col = (col_lo.0 as u64) & 0xFF; - let line = ((line_lo as u64) & 0xFF_FF_FF) << 8; - let len = ((span.hi - span.lo).0 as u64) << 32; - let line_col_len = col | line | len; - std::hash::Hash::hash(&line_col_len, hasher); - - if span.ctxt == SyntaxContext::root() { - TAG_NO_EXPANSION.hash_stable(ctx, hasher); - } else { - TAG_EXPANSION.hash_stable(ctx, hasher); - - // Since the same expansion context is usually referenced many - // times, we cache a stable hash of it and hash that instead of - // recursing every time. - thread_local! { - static CACHE: RefCell<FxHashMap<hygiene::ExpnId, u64>> = Default::default(); - } - - let sub_hash: u64 = CACHE.with(|cache| { - let expn_id = span.ctxt.outer_expn(); - - if let Some(&sub_hash) = cache.borrow().get(&expn_id) { - return sub_hash; - } - - let mut hasher = StableHasher::new(); - expn_id.expn_data().hash_stable(ctx, &mut hasher); - let sub_hash: Fingerprint = hasher.finish(); - let sub_hash = sub_hash.to_smaller_hash(); - cache.borrow_mut().insert(expn_id, sub_hash); - sub_hash - }); - - sub_hash.hash_stable(ctx, hasher); - } - } -} diff --git a/src/libsyntax_pos/source_map.rs b/src/libsyntax_pos/source_map.rs deleted file mode 100644 index 0b9b9fe7887..00000000000 --- a/src/libsyntax_pos/source_map.rs +++ /dev/null @@ -1,984 +0,0 @@ -//! The `SourceMap` tracks all the source code used within a single crate, mapping -//! from integer byte positions to the original source code location. Each bit -//! of source parsed during crate parsing (typically files, in-memory strings, -//! or various bits of macro expansion) cover a continuous range of bytes in the -//! `SourceMap` and are represented by `SourceFile`s. Byte positions are stored in -//! `Span` and used pervasively in the compiler. They are absolute positions -//! within the `SourceMap`, which upon request can be converted to line and column -//! information, source code snippets, etc. - -pub use crate::hygiene::{ExpnData, ExpnKind}; -pub use crate::*; - -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::StableHasher; -use rustc_data_structures::sync::{Lock, LockGuard, Lrc, MappedLockGuard}; -use std::cmp; -use std::hash::Hash; -use std::path::{Path, PathBuf}; - -use log::debug; -use std::env; -use std::fs; -use std::io; - -#[cfg(test)] -mod tests; - -/// Returns 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_data` chain. -pub fn original_sp(sp: Span, enclosing_sp: Span) -> Span { - let expn_data1 = sp.ctxt().outer_expn_data(); - let expn_data2 = enclosing_sp.ctxt().outer_expn_data(); - if expn_data1.is_root() || !expn_data2.is_root() && expn_data1.call_site == expn_data2.call_site - { - sp - } else { - original_sp(expn_data1.call_site, enclosing_sp) - } -} - -#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy, HashStable_Generic)] -pub struct Spanned<T> { - pub node: T, - pub span: Span, -} - -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) -} - -// _____________________________________________________________________________ -// SourceFile, MultiByteChar, FileName, FileLines -// - -/// An abstraction over the fs operations used by the Parser. -pub trait FileLoader { - /// Query the existence of a file. - fn file_exists(&self, path: &Path) -> bool; - - /// Returns an absolute path to a file, if possible. - fn abs_path(&self, path: &Path) -> Option<PathBuf>; - - /// Read the contents of an UTF-8 file into memory. - fn read_file(&self, path: &Path) -> io::Result<String>; -} - -/// A FileLoader that uses std::fs to load real files. -pub struct RealFileLoader; - -impl FileLoader for RealFileLoader { - fn file_exists(&self, path: &Path) -> bool { - fs::metadata(path).is_ok() - } - - fn abs_path(&self, path: &Path) -> Option<PathBuf> { - if path.is_absolute() { - Some(path.to_path_buf()) - } else { - env::current_dir().ok().map(|cwd| cwd.join(path)) - } - } - - fn read_file(&self, path: &Path) -> io::Result<String> { - fs::read_to_string(path) - } -} - -// This is a `SourceFile` identifier that is used to correlate `SourceFile`s between -// subsequent compilation sessions (which is something we need to do during -// incremental compilation). -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] -pub struct StableSourceFileId(u128); - -impl StableSourceFileId { - pub fn new(source_file: &SourceFile) -> StableSourceFileId { - StableSourceFileId::new_from_pieces( - &source_file.name, - source_file.name_was_remapped, - source_file.unmapped_path.as_ref(), - ) - } - - pub fn new_from_pieces( - name: &FileName, - name_was_remapped: bool, - unmapped_path: Option<&FileName>, - ) -> StableSourceFileId { - let mut hasher = StableHasher::new(); - - name.hash(&mut hasher); - name_was_remapped.hash(&mut hasher); - unmapped_path.hash(&mut hasher); - - StableSourceFileId(hasher.finish()) - } -} - -// _____________________________________________________________________________ -// SourceMap -// - -#[derive(Default)] -pub(super) struct SourceMapFiles { - source_files: Vec<Lrc<SourceFile>>, - stable_id_to_source_file: FxHashMap<StableSourceFileId, Lrc<SourceFile>>, -} - -pub struct SourceMap { - files: Lock<SourceMapFiles>, - file_loader: Box<dyn FileLoader + Sync + Send>, - // This is used to apply the file path remapping as specified via - // `--remap-path-prefix` to all `SourceFile`s allocated within this `SourceMap`. - path_mapping: FilePathMapping, -} - -impl SourceMap { - pub fn new(path_mapping: FilePathMapping) -> SourceMap { - SourceMap { files: Default::default(), file_loader: Box::new(RealFileLoader), path_mapping } - } - - pub fn with_file_loader( - file_loader: Box<dyn FileLoader + Sync + Send>, - path_mapping: FilePathMapping, - ) -> SourceMap { - SourceMap { files: Default::default(), file_loader, path_mapping } - } - - pub fn path_mapping(&self) -> &FilePathMapping { - &self.path_mapping - } - - pub fn file_exists(&self, path: &Path) -> bool { - self.file_loader.file_exists(path) - } - - pub fn load_file(&self, path: &Path) -> io::Result<Lrc<SourceFile>> { - let src = self.file_loader.read_file(path)?; - let filename = path.to_owned().into(); - Ok(self.new_source_file(filename, src)) - } - - /// Loads source file as a binary blob. - /// - /// Unlike `load_file`, guarantees that no normalization like BOM-removal - /// takes place. - pub fn load_binary_file(&self, path: &Path) -> io::Result<Vec<u8>> { - // Ideally, this should use `self.file_loader`, but it can't - // deal with binary files yet. - let bytes = fs::read(path)?; - - // We need to add file to the `SourceMap`, so that it is present - // in dep-info. There's also an edge case that file might be both - // loaded as a binary via `include_bytes!` and as proper `SourceFile` - // via `mod`, so we try to use real file contents and not just an - // empty string. - let text = std::str::from_utf8(&bytes).unwrap_or("").to_string(); - self.new_source_file(path.to_owned().into(), text); - Ok(bytes) - } - - pub fn files(&self) -> MappedLockGuard<'_, Vec<Lrc<SourceFile>>> { - LockGuard::map(self.files.borrow(), |files| &mut files.source_files) - } - - pub fn source_file_by_stable_id( - &self, - stable_id: StableSourceFileId, - ) -> Option<Lrc<SourceFile>> { - self.files.borrow().stable_id_to_source_file.get(&stable_id).map(|sf| sf.clone()) - } - - fn next_start_pos(&self) -> usize { - match self.files.borrow().source_files.last() { - None => 0, - // Add one so there is some space between files. This lets us distinguish - // positions in the `SourceMap`, even in the presence of zero-length files. - Some(last) => last.end_pos.to_usize() + 1, - } - } - - /// Creates a new `SourceFile`. - /// If a file already exists in the `SourceMap` with the same ID, that file is returned - /// unmodified. - pub fn new_source_file(&self, filename: FileName, src: String) -> Lrc<SourceFile> { - self.try_new_source_file(filename, src).unwrap_or_else(|OffsetOverflowError| { - eprintln!("fatal error: rustc does not support files larger than 4GB"); - crate::fatal_error::FatalError.raise() - }) - } - - fn try_new_source_file( - &self, - filename: FileName, - src: String, - ) -> Result<Lrc<SourceFile>, OffsetOverflowError> { - let start_pos = self.next_start_pos(); - - // The path is used to determine the directory for loading submodules and - // include files, so it must be before remapping. - // Note that filename may not be a valid path, eg it may be `<anon>` etc, - // but this is okay because the directory determined by `path.pop()` will - // be empty, so the working directory will be used. - let unmapped_path = filename.clone(); - - let (filename, was_remapped) = match filename { - FileName::Real(filename) => { - let (filename, was_remapped) = self.path_mapping.map_prefix(filename); - (FileName::Real(filename), was_remapped) - } - other => (other, false), - }; - - let file_id = - StableSourceFileId::new_from_pieces(&filename, was_remapped, Some(&unmapped_path)); - - let lrc_sf = match self.source_file_by_stable_id(file_id) { - Some(lrc_sf) => lrc_sf, - None => { - let source_file = Lrc::new(SourceFile::new( - filename, - was_remapped, - unmapped_path, - src, - Pos::from_usize(start_pos), - )?); - - let mut files = self.files.borrow_mut(); - - files.source_files.push(source_file.clone()); - files.stable_id_to_source_file.insert(file_id, source_file.clone()); - - source_file - } - }; - Ok(lrc_sf) - } - - /// Allocates a new `SourceFile` representing a source file from an external - /// crate. The source code of such an "imported `SourceFile`" is not available, - /// but we still know enough to generate accurate debuginfo location - /// information for things inlined from other crates. - pub fn new_imported_source_file( - &self, - filename: FileName, - name_was_remapped: bool, - crate_of_origin: u32, - src_hash: u128, - name_hash: u128, - source_len: usize, - mut file_local_lines: Vec<BytePos>, - mut file_local_multibyte_chars: Vec<MultiByteChar>, - mut file_local_non_narrow_chars: Vec<NonNarrowChar>, - mut file_local_normalized_pos: Vec<NormalizedPos>, - ) -> Lrc<SourceFile> { - let start_pos = self.next_start_pos(); - - let end_pos = Pos::from_usize(start_pos + source_len); - let start_pos = Pos::from_usize(start_pos); - - for pos in &mut file_local_lines { - *pos = *pos + start_pos; - } - - for mbc in &mut file_local_multibyte_chars { - mbc.pos = mbc.pos + start_pos; - } - - for swc in &mut file_local_non_narrow_chars { - *swc = *swc + start_pos; - } - - for nc in &mut file_local_normalized_pos { - nc.pos = nc.pos + start_pos; - } - - let source_file = Lrc::new(SourceFile { - name: filename, - name_was_remapped, - unmapped_path: None, - crate_of_origin, - src: None, - src_hash, - external_src: Lock::new(ExternalSource::AbsentOk), - start_pos, - end_pos, - lines: file_local_lines, - multibyte_chars: file_local_multibyte_chars, - non_narrow_chars: file_local_non_narrow_chars, - normalized_pos: file_local_normalized_pos, - name_hash, - }); - - let mut files = self.files.borrow_mut(); - - files.source_files.push(source_file.clone()); - files - .stable_id_to_source_file - .insert(StableSourceFileId::new(&source_file), source_file.clone()); - - source_file - } - - pub fn mk_substr_filename(&self, sp: Span) -> String { - let pos = self.lookup_char_pos(sp.lo()); - format!("<{}:{}:{}>", pos.file.name, pos.line, pos.col.to_usize() + 1) - } - - // If there is a doctest offset, applies it to the line. - pub fn doctest_offset_line(&self, file: &FileName, orig: usize) -> usize { - return match file { - FileName::DocTest(_, offset) => { - return if *offset >= 0 { - orig + *offset as usize - } else { - orig - (-(*offset)) as usize - }; - } - _ => orig, - }; - } - - /// Looks up source information about a `BytePos`. - pub fn lookup_char_pos(&self, pos: BytePos) -> Loc { - let chpos = self.bytepos_to_file_charpos(pos); - match self.lookup_line(pos) { - Ok(SourceFileAndLine { sf: f, line: a }) => { - let line = a + 1; // Line numbers start at 1 - let linebpos = f.lines[a]; - let linechpos = self.bytepos_to_file_charpos(linebpos); - let col = chpos - linechpos; - - let col_display = { - let start_width_idx = f - .non_narrow_chars - .binary_search_by_key(&linebpos, |x| x.pos()) - .unwrap_or_else(|x| x); - let end_width_idx = f - .non_narrow_chars - .binary_search_by_key(&pos, |x| x.pos()) - .unwrap_or_else(|x| x); - let special_chars = end_width_idx - start_width_idx; - let non_narrow: usize = f.non_narrow_chars[start_width_idx..end_width_idx] - .into_iter() - .map(|x| x.width()) - .sum(); - col.0 - special_chars + non_narrow - }; - debug!("byte pos {:?} is on the line at byte pos {:?}", pos, linebpos); - debug!("char pos {:?} is on the line at char pos {:?}", chpos, linechpos); - debug!("byte is on line: {}", line); - assert!(chpos >= linechpos); - Loc { file: f, line, col, col_display } - } - Err(f) => { - let col_display = { - let end_width_idx = f - .non_narrow_chars - .binary_search_by_key(&pos, |x| x.pos()) - .unwrap_or_else(|x| x); - let non_narrow: usize = - f.non_narrow_chars[0..end_width_idx].into_iter().map(|x| x.width()).sum(); - chpos.0 - end_width_idx + non_narrow - }; - Loc { file: f, line: 0, col: chpos, col_display } - } - } - } - - // If the corresponding `SourceFile` is empty, does not return a line number. - pub fn lookup_line(&self, pos: BytePos) -> Result<SourceFileAndLine, Lrc<SourceFile>> { - let idx = self.lookup_source_file_idx(pos); - - let f = (*self.files.borrow().source_files)[idx].clone(); - - match f.lookup_line(pos) { - Some(line) => Ok(SourceFileAndLine { sf: f, line }), - None => Err(f), - } - } - - /// Returns `Some(span)`, a union of the LHS and RHS span. The LHS must precede the RHS. If - /// there are gaps between LHS and RHS, the resulting union will cross these gaps. - /// For this to work, - /// - /// * the syntax contexts of both spans much match, - /// * the LHS span needs to end on the same line the RHS span begins, - /// * the LHS span must start at or before the RHS span. - pub fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> { - // Ensure we're at the same expansion ID. - if sp_lhs.ctxt() != sp_rhs.ctxt() { - return None; - } - - let lhs_end = match self.lookup_line(sp_lhs.hi()) { - Ok(x) => x, - Err(_) => return None, - }; - let rhs_begin = match self.lookup_line(sp_rhs.lo()) { - Ok(x) => x, - Err(_) => return None, - }; - - // If we must cross lines to merge, don't merge. - if lhs_end.line != rhs_begin.line { - return None; - } - - // Ensure these follow the expected order and that we don't overlap. - if (sp_lhs.lo() <= sp_rhs.lo()) && (sp_lhs.hi() <= sp_rhs.lo()) { - Some(sp_lhs.to(sp_rhs)) - } else { - None - } - } - - pub fn span_to_string(&self, sp: Span) -> String { - if self.files.borrow().source_files.is_empty() && sp.is_dummy() { - return "no-location".to_string(); - } - - let lo = self.lookup_char_pos(sp.lo()); - let hi = self.lookup_char_pos(sp.hi()); - format!( - "{}:{}:{}: {}:{}", - lo.file.name, - lo.line, - lo.col.to_usize() + 1, - hi.line, - hi.col.to_usize() + 1, - ) - } - - pub fn span_to_filename(&self, sp: Span) -> FileName { - self.lookup_char_pos(sp.lo()).file.name.clone() - } - - pub fn span_to_unmapped_path(&self, sp: Span) -> FileName { - self.lookup_char_pos(sp.lo()) - .file - .unmapped_path - .clone() - .expect("`SourceMap::span_to_unmapped_path` called for imported `SourceFile`?") - } - - pub fn is_multiline(&self, sp: Span) -> bool { - let lo = self.lookup_char_pos(sp.lo()); - let hi = self.lookup_char_pos(sp.hi()); - lo.line != hi.line - } - - pub fn span_to_lines(&self, sp: Span) -> FileLinesResult { - debug!("span_to_lines(sp={:?})", sp); - - let lo = self.lookup_char_pos(sp.lo()); - debug!("span_to_lines: lo={:?}", lo); - let hi = self.lookup_char_pos(sp.hi()); - debug!("span_to_lines: hi={:?}", hi); - - if lo.file.start_pos != hi.file.start_pos { - return Err(SpanLinesError::DistinctSources(DistinctSources { - begin: (lo.file.name.clone(), lo.file.start_pos), - end: (hi.file.name.clone(), hi.file.start_pos), - })); - } - assert!(hi.line >= lo.line); - - let mut lines = Vec::with_capacity(hi.line - lo.line + 1); - - // The span starts partway through the first line, - // but after that it starts from offset 0. - let mut start_col = lo.col; - - // For every line but the last, it extends from `start_col` - // and to the end of the line. Be careful because the line - // numbers in Loc are 1-based, so we subtract 1 to get 0-based - // lines. - for line_index in lo.line - 1..hi.line - 1 { - let line_len = lo.file.get_line(line_index).map(|s| s.chars().count()).unwrap_or(0); - lines.push(LineInfo { line_index, start_col, end_col: CharPos::from_usize(line_len) }); - start_col = CharPos::from_usize(0); - } - - // For the last line, it extends from `start_col` to `hi.col`: - lines.push(LineInfo { line_index: hi.line - 1, start_col, end_col: hi.col }); - - Ok(FileLines { file: lo.file, lines }) - } - - /// Extracts the source surrounding the given `Span` using the `extract_source` function. The - /// extract function takes three arguments: a string slice containing the source, an index in - /// the slice for the beginning of the span and an index in the slice for the end of the span. - fn span_to_source<F>(&self, sp: Span, extract_source: F) -> Result<String, SpanSnippetError> - where - F: Fn(&str, usize, usize) -> Result<String, SpanSnippetError>, - { - let local_begin = self.lookup_byte_offset(sp.lo()); - let local_end = self.lookup_byte_offset(sp.hi()); - - if local_begin.sf.start_pos != local_end.sf.start_pos { - return Err(SpanSnippetError::DistinctSources(DistinctSources { - begin: (local_begin.sf.name.clone(), local_begin.sf.start_pos), - end: (local_end.sf.name.clone(), local_end.sf.start_pos), - })); - } else { - self.ensure_source_file_source_present(local_begin.sf.clone()); - - let start_index = local_begin.pos.to_usize(); - let end_index = local_end.pos.to_usize(); - let source_len = (local_begin.sf.end_pos - local_begin.sf.start_pos).to_usize(); - - if start_index > end_index || end_index > source_len { - return Err(SpanSnippetError::MalformedForSourcemap(MalformedSourceMapPositions { - name: local_begin.sf.name.clone(), - source_len, - begin_pos: local_begin.pos, - end_pos: local_end.pos, - })); - } - - if let Some(ref src) = local_begin.sf.src { - return extract_source(src, start_index, end_index); - } else if let Some(src) = local_begin.sf.external_src.borrow().get_source() { - return extract_source(src, start_index, end_index); - } else { - return Err(SpanSnippetError::SourceNotAvailable { - filename: local_begin.sf.name.clone(), - }); - } - } - } - - /// Returns the source snippet as `String` corresponding to the given `Span`. - pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> { - self.span_to_source(sp, |src, start_index, end_index| { - src.get(start_index..end_index) - .map(|s| s.to_string()) - .ok_or_else(|| SpanSnippetError::IllFormedSpan(sp)) - }) - } - - pub fn span_to_margin(&self, sp: Span) -> Option<usize> { - match self.span_to_prev_source(sp) { - Err(_) => None, - Ok(source) => source - .split('\n') - .last() - .map(|last_line| last_line.len() - last_line.trim_start().len()), - } - } - - /// Returns the source snippet as `String` before the given `Span`. - pub fn span_to_prev_source(&self, sp: Span) -> Result<String, SpanSnippetError> { - self.span_to_source(sp, |src, start_index, _| { - src.get(..start_index) - .map(|s| s.to_string()) - .ok_or_else(|| SpanSnippetError::IllFormedSpan(sp)) - }) - } - - /// Extends the given `Span` to just after the previous occurrence of `c`. Return the same span - /// if no character could be found or if an error occurred while retrieving the code snippet. - pub fn span_extend_to_prev_char(&self, sp: Span, c: char) -> Span { - if let Ok(prev_source) = self.span_to_prev_source(sp) { - let prev_source = prev_source.rsplit(c).nth(0).unwrap_or("").trim_start(); - if !prev_source.is_empty() && !prev_source.contains('\n') { - return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32)); - } - } - - sp - } - - /// Extends the given `Span` to just after the previous occurrence of `pat` when surrounded by - /// whitespace. Returns the same span if no character could be found or if an error occurred - /// while retrieving the code snippet. - pub fn span_extend_to_prev_str(&self, sp: Span, pat: &str, accept_newlines: bool) -> Span { - // assure that the pattern is delimited, to avoid the following - // fn my_fn() - // ^^^^ returned span without the check - // ---------- correct span - for ws in &[" ", "\t", "\n"] { - let pat = pat.to_owned() + ws; - if let Ok(prev_source) = self.span_to_prev_source(sp) { - let prev_source = prev_source.rsplit(&pat).nth(0).unwrap_or("").trim_start(); - if !prev_source.is_empty() && (!prev_source.contains('\n') || accept_newlines) { - return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32)); - } - } - } - - sp - } - - /// Given a `Span`, tries to get a shorter span ending before the first occurrence of `char` - /// `c`. - pub fn span_until_char(&self, sp: Span, c: char) -> Span { - match self.span_to_snippet(sp) { - Ok(snippet) => { - let snippet = snippet.split(c).nth(0).unwrap_or("").trim_end(); - if !snippet.is_empty() && !snippet.contains('\n') { - sp.with_hi(BytePos(sp.lo().0 + snippet.len() as u32)) - } else { - sp - } - } - _ => sp, - } - } - - /// Given a `Span`, tries to get a shorter span ending just after the first occurrence of `char` - /// `c`. - pub fn span_through_char(&self, sp: Span, c: char) -> Span { - if let Ok(snippet) = self.span_to_snippet(sp) { - if let Some(offset) = snippet.find(c) { - return sp.with_hi(BytePos(sp.lo().0 + (offset + c.len_utf8()) as u32)); - } - } - sp - } - - /// Given a `Span`, gets a new `Span` covering the first token and all its trailing whitespace - /// or the original `Span`. - /// - /// If `sp` points to `"let mut x"`, then a span pointing at `"let "` will be returned. - pub fn span_until_non_whitespace(&self, sp: Span) -> Span { - let mut whitespace_found = false; - - self.span_take_while(sp, |c| { - if !whitespace_found && c.is_whitespace() { - whitespace_found = true; - } - - if whitespace_found && !c.is_whitespace() { false } else { true } - }) - } - - /// Given a `Span`, gets a new `Span` covering the first token without its trailing whitespace - /// or the original `Span` in case of error. - /// - /// If `sp` points to `"let mut x"`, then a span pointing at `"let"` will be returned. - pub fn span_until_whitespace(&self, sp: Span) -> Span { - self.span_take_while(sp, |c| !c.is_whitespace()) - } - - /// Given a `Span`, gets a shorter one until `predicate` yields `false`. - pub fn span_take_while<P>(&self, sp: Span, predicate: P) -> Span - where - P: for<'r> FnMut(&'r char) -> bool, - { - if let Ok(snippet) = self.span_to_snippet(sp) { - let offset = snippet.chars().take_while(predicate).map(|c| c.len_utf8()).sum::<usize>(); - - sp.with_hi(BytePos(sp.lo().0 + (offset as u32))) - } else { - sp - } - } - - pub fn def_span(&self, sp: Span) -> Span { - self.span_until_char(sp, '{') - } - - /// Returns a new span representing just the start point of this span. - pub fn start_point(&self, sp: Span) -> Span { - let pos = sp.lo().0; - let width = self.find_width_of_character_at_span(sp, false); - let corrected_start_position = pos.checked_add(width).unwrap_or(pos); - let end_point = BytePos(cmp::max(corrected_start_position, sp.lo().0)); - sp.with_hi(end_point) - } - - /// Returns a new span representing just the end point of this span. - pub fn end_point(&self, sp: Span) -> Span { - let pos = sp.hi().0; - - let width = self.find_width_of_character_at_span(sp, false); - let corrected_end_position = pos.checked_sub(width).unwrap_or(pos); - - let end_point = BytePos(cmp::max(corrected_end_position, sp.lo().0)); - sp.with_lo(end_point) - } - - /// Returns a new span representing the next character after the end-point of this span. - pub fn next_point(&self, sp: Span) -> Span { - let start_of_next_point = sp.hi().0; - - let width = self.find_width_of_character_at_span(sp, true); - // If the width is 1, then the next span should point to the same `lo` and `hi`. However, - // in the case of a multibyte character, where the width != 1, the next span should - // span multiple bytes to include the whole character. - let end_of_next_point = - start_of_next_point.checked_add(width - 1).unwrap_or(start_of_next_point); - - let end_of_next_point = BytePos(cmp::max(sp.lo().0 + 1, end_of_next_point)); - Span::new(BytePos(start_of_next_point), end_of_next_point, sp.ctxt()) - } - - /// Finds the width of a character, either before or after the provided span. - fn find_width_of_character_at_span(&self, sp: Span, forwards: bool) -> u32 { - let sp = sp.data(); - if sp.lo == sp.hi { - debug!("find_width_of_character_at_span: early return empty span"); - return 1; - } - - let local_begin = self.lookup_byte_offset(sp.lo); - let local_end = self.lookup_byte_offset(sp.hi); - debug!( - "find_width_of_character_at_span: local_begin=`{:?}`, local_end=`{:?}`", - local_begin, local_end - ); - - if local_begin.sf.start_pos != local_end.sf.start_pos { - debug!("find_width_of_character_at_span: begin and end are in different files"); - return 1; - } - - let start_index = local_begin.pos.to_usize(); - let end_index = local_end.pos.to_usize(); - debug!( - "find_width_of_character_at_span: start_index=`{:?}`, end_index=`{:?}`", - start_index, end_index - ); - - // Disregard indexes that are at the start or end of their spans, they can't fit bigger - // characters. - if (!forwards && end_index == usize::min_value()) - || (forwards && start_index == usize::max_value()) - { - debug!("find_width_of_character_at_span: start or end of span, cannot be multibyte"); - return 1; - } - - let source_len = (local_begin.sf.end_pos - local_begin.sf.start_pos).to_usize(); - debug!("find_width_of_character_at_span: source_len=`{:?}`", source_len); - // Ensure indexes are also not malformed. - if start_index > end_index || end_index > source_len { - debug!("find_width_of_character_at_span: source indexes are malformed"); - return 1; - } - - let src = local_begin.sf.external_src.borrow(); - - // We need to extend the snippet to the end of the src rather than to end_index so when - // searching forwards for boundaries we've got somewhere to search. - let snippet = if let Some(ref src) = local_begin.sf.src { - let len = src.len(); - (&src[start_index..len]) - } else if let Some(src) = src.get_source() { - let len = src.len(); - (&src[start_index..len]) - } else { - return 1; - }; - debug!("find_width_of_character_at_span: snippet=`{:?}`", snippet); - - let mut target = if forwards { end_index + 1 } else { end_index - 1 }; - debug!("find_width_of_character_at_span: initial target=`{:?}`", target); - - while !snippet.is_char_boundary(target - start_index) && target < source_len { - target = if forwards { - target + 1 - } else { - match target.checked_sub(1) { - Some(target) => target, - None => { - break; - } - } - }; - debug!("find_width_of_character_at_span: target=`{:?}`", target); - } - debug!("find_width_of_character_at_span: final target=`{:?}`", target); - - if forwards { (target - end_index) as u32 } else { (end_index - target) as u32 } - } - - pub fn get_source_file(&self, filename: &FileName) -> Option<Lrc<SourceFile>> { - for sf in self.files.borrow().source_files.iter() { - if *filename == sf.name { - return Some(sf.clone()); - } - } - None - } - - /// For a global `BytePos`, computes the local offset within the containing `SourceFile`. - pub fn lookup_byte_offset(&self, bpos: BytePos) -> SourceFileAndBytePos { - let idx = self.lookup_source_file_idx(bpos); - let sf = (*self.files.borrow().source_files)[idx].clone(); - let offset = bpos - sf.start_pos; - SourceFileAndBytePos { sf, pos: offset } - } - - /// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`. - pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos { - let idx = self.lookup_source_file_idx(bpos); - let map = &(*self.files.borrow().source_files)[idx]; - - // The number of extra bytes due to multibyte chars in the `SourceFile`. - let mut total_extra_bytes = 0; - - for mbc in map.multibyte_chars.iter() { - debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos); - if mbc.pos < bpos { - // Every character is at least one byte, so we only - // count the actual extra bytes. - total_extra_bytes += mbc.bytes as u32 - 1; - // We should never see a byte position in the middle of a - // character. - assert!(bpos.to_u32() >= mbc.pos.to_u32() + mbc.bytes as u32); - } else { - break; - } - } - - assert!(map.start_pos.to_u32() + total_extra_bytes <= bpos.to_u32()); - CharPos(bpos.to_usize() - map.start_pos.to_usize() - total_extra_bytes as usize) - } - - // Returns the index of the `SourceFile` (in `self.files`) that contains `pos`. - pub fn lookup_source_file_idx(&self, pos: BytePos) -> usize { - self.files - .borrow() - .source_files - .binary_search_by_key(&pos, |key| key.start_pos) - .unwrap_or_else(|p| p - 1) - } - - pub fn count_lines(&self) -> usize { - self.files().iter().fold(0, |a, f| a + f.count_lines()) - } - - pub fn generate_fn_name_span(&self, span: Span) -> Option<Span> { - let prev_span = self.span_extend_to_prev_str(span, "fn", true); - self.span_to_snippet(prev_span) - .map(|snippet| { - let len = snippet - .find(|c: char| !c.is_alphanumeric() && c != '_') - .expect("no label after fn"); - prev_span.with_hi(BytePos(prev_span.lo().0 + len as u32)) - }) - .ok() - } - - /// Takes the span of a type parameter in a function signature and try to generate a span for - /// the function name (with generics) and a new snippet for this span with the pointed type - /// parameter as a new local type parameter. - /// - /// For instance: - /// ```rust,ignore (pseudo-Rust) - /// // Given span - /// fn my_function(param: T) - /// // ^ Original span - /// - /// // Result - /// fn my_function(param: T) - /// // ^^^^^^^^^^^ Generated span with snippet `my_function<T>` - /// ``` - /// - /// Attention: The method used is very fragile since it essentially duplicates the work of the - /// parser. If you need to use this function or something similar, please consider updating the - /// `SourceMap` functions and this function to something more robust. - pub fn generate_local_type_param_snippet(&self, span: Span) -> Option<(Span, String)> { - // Try to extend the span to the previous "fn" keyword to retrieve the function - // signature. - let sugg_span = self.span_extend_to_prev_str(span, "fn", false); - if sugg_span != span { - if let Ok(snippet) = self.span_to_snippet(sugg_span) { - // Consume the function name. - let mut offset = snippet - .find(|c: char| !c.is_alphanumeric() && c != '_') - .expect("no label after fn"); - - // Consume the generics part of the function signature. - let mut bracket_counter = 0; - let mut last_char = None; - for c in snippet[offset..].chars() { - match c { - '<' => bracket_counter += 1, - '>' => bracket_counter -= 1, - '(' => { - if bracket_counter == 0 { - break; - } - } - _ => {} - } - offset += c.len_utf8(); - last_char = Some(c); - } - - // Adjust the suggestion span to encompass the function name with its generics. - let sugg_span = sugg_span.with_hi(BytePos(sugg_span.lo().0 + offset as u32)); - - // Prepare the new suggested snippet to append the type parameter that triggered - // the error in the generics of the function signature. - let mut new_snippet = if last_char == Some('>') { - format!("{}, ", &snippet[..(offset - '>'.len_utf8())]) - } else { - format!("{}<", &snippet[..offset]) - }; - new_snippet - .push_str(&self.span_to_snippet(span).unwrap_or_else(|_| "T".to_string())); - new_snippet.push('>'); - - return Some((sugg_span, new_snippet)); - } - } - - None - } - pub fn ensure_source_file_source_present(&self, source_file: Lrc<SourceFile>) -> bool { - source_file.add_external_src(|| match source_file.name { - FileName::Real(ref name) => self.file_loader.read_file(name).ok(), - _ => None, - }) - } - pub fn call_span_if_macro(&self, sp: Span) -> Span { - if self.span_to_filename(sp.clone()).is_macros() { - let v = sp.macro_backtrace(); - if let Some(use_site) = v.last() { - return use_site.call_site; - } - } - sp - } -} - -#[derive(Clone)] -pub struct FilePathMapping { - mapping: Vec<(PathBuf, PathBuf)>, -} - -impl FilePathMapping { - pub fn empty() -> FilePathMapping { - FilePathMapping { mapping: vec![] } - } - - pub fn new(mapping: Vec<(PathBuf, PathBuf)>) -> FilePathMapping { - FilePathMapping { mapping } - } - - /// Applies any path prefix substitution as defined by the mapping. - /// The return value is the remapped path and a boolean indicating whether - /// the path was affected by the mapping. - pub fn map_prefix(&self, path: PathBuf) -> (PathBuf, bool) { - // NOTE: We are iterating over the mapping entries from last to first - // because entries specified later on the command line should - // take precedence. - for &(ref from, ref to) in self.mapping.iter().rev() { - if let Ok(rest) = path.strip_prefix(from) { - return (to.join(rest), true); - } - } - - (path, false) - } -} diff --git a/src/libsyntax_pos/source_map/tests.rs b/src/libsyntax_pos/source_map/tests.rs deleted file mode 100644 index 79df1884f0d..00000000000 --- a/src/libsyntax_pos/source_map/tests.rs +++ /dev/null @@ -1,216 +0,0 @@ -use super::*; - -use rustc_data_structures::sync::Lrc; - -fn init_source_map() -> SourceMap { - let sm = SourceMap::new(FilePathMapping::empty()); - sm.new_source_file(PathBuf::from("blork.rs").into(), "first line.\nsecond line".to_string()); - sm.new_source_file(PathBuf::from("empty.rs").into(), String::new()); - sm.new_source_file(PathBuf::from("blork2.rs").into(), "first line.\nsecond line".to_string()); - sm -} - -/// Tests `lookup_byte_offset`. -#[test] -fn t3() { - let sm = init_source_map(); - - let srcfbp1 = sm.lookup_byte_offset(BytePos(23)); - assert_eq!(srcfbp1.sf.name, PathBuf::from("blork.rs").into()); - assert_eq!(srcfbp1.pos, BytePos(23)); - - let srcfbp1 = sm.lookup_byte_offset(BytePos(24)); - assert_eq!(srcfbp1.sf.name, PathBuf::from("empty.rs").into()); - assert_eq!(srcfbp1.pos, BytePos(0)); - - let srcfbp2 = sm.lookup_byte_offset(BytePos(25)); - assert_eq!(srcfbp2.sf.name, PathBuf::from("blork2.rs").into()); - assert_eq!(srcfbp2.pos, BytePos(0)); -} - -/// Tests `bytepos_to_file_charpos`. -#[test] -fn t4() { - let sm = init_source_map(); - - let cp1 = sm.bytepos_to_file_charpos(BytePos(22)); - assert_eq!(cp1, CharPos(22)); - - let cp2 = sm.bytepos_to_file_charpos(BytePos(25)); - assert_eq!(cp2, CharPos(0)); -} - -/// Tests zero-length `SourceFile`s. -#[test] -fn t5() { - let sm = init_source_map(); - - let loc1 = sm.lookup_char_pos(BytePos(22)); - assert_eq!(loc1.file.name, PathBuf::from("blork.rs").into()); - assert_eq!(loc1.line, 2); - assert_eq!(loc1.col, CharPos(10)); - - let loc2 = sm.lookup_char_pos(BytePos(25)); - assert_eq!(loc2.file.name, PathBuf::from("blork2.rs").into()); - assert_eq!(loc2.line, 1); - assert_eq!(loc2.col, CharPos(0)); -} - -fn init_source_map_mbc() -> SourceMap { - let sm = SourceMap::new(FilePathMapping::empty()); - // "€" is a three-byte UTF8 char. - sm.new_source_file( - PathBuf::from("blork.rs").into(), - "fir€st €€€€ line.\nsecond line".to_string(), - ); - sm.new_source_file( - PathBuf::from("blork2.rs").into(), - "first line€€.\n€ second line".to_string(), - ); - sm -} - -/// Tests `bytepos_to_file_charpos` in the presence of multi-byte chars. -#[test] -fn t6() { - let sm = init_source_map_mbc(); - - let cp1 = sm.bytepos_to_file_charpos(BytePos(3)); - assert_eq!(cp1, CharPos(3)); - - let cp2 = sm.bytepos_to_file_charpos(BytePos(6)); - assert_eq!(cp2, CharPos(4)); - - let cp3 = sm.bytepos_to_file_charpos(BytePos(56)); - assert_eq!(cp3, CharPos(12)); - - let cp4 = sm.bytepos_to_file_charpos(BytePos(61)); - assert_eq!(cp4, CharPos(15)); -} - -/// Test `span_to_lines` for a span ending at the end of a `SourceFile`. -#[test] -fn t7() { - let sm = init_source_map(); - let span = Span::with_root_ctxt(BytePos(12), BytePos(23)); - let file_lines = sm.span_to_lines(span).unwrap(); - - assert_eq!(file_lines.file.name, PathBuf::from("blork.rs").into()); - assert_eq!(file_lines.lines.len(), 1); - assert_eq!(file_lines.lines[0].line_index, 1); -} - -/// Given a string like " ~~~~~~~~~~~~ ", produces a span -/// converting 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::with_root_ctxt(BytePos(left_index), BytePos(right_index + 1)) -} - -/// Tests `span_to_snippet` and `span_to_lines` for a span converting 3 -/// lines in the middle of a file. -#[test] -fn span_to_snippet_and_lines_spanning_multiple_lines() { - let sm = SourceMap::new(FilePathMapping::empty()); - let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; - let selection = " \n ~~\n~~~\n~~~~~ \n \n"; - sm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_string()); - let span = span_from_selection(inputtext, selection); - - // Check that we are extracting the text we thought we were extracting. - assert_eq!(&sm.span_to_snippet(span).unwrap(), "BB\nCCC\nDDDDD"); - - // Check that span_to_lines gives us the complete result with the lines/cols we expected. - let lines = sm.span_to_lines(span).unwrap(); - let expected = vec![ - LineInfo { line_index: 1, start_col: CharPos(4), end_col: CharPos(6) }, - LineInfo { line_index: 2, start_col: CharPos(0), end_col: CharPos(3) }, - LineInfo { line_index: 3, start_col: CharPos(0), end_col: CharPos(5) }, - ]; - assert_eq!(lines.lines, expected); -} - -/// Test span_to_snippet for a span ending at the end of a `SourceFile`. -#[test] -fn t8() { - let sm = init_source_map(); - let span = Span::with_root_ctxt(BytePos(12), BytePos(23)); - let snippet = sm.span_to_snippet(span); - - assert_eq!(snippet, Ok("second line".to_string())); -} - -/// Test `span_to_str` for a span ending at the end of a `SourceFile`. -#[test] -fn t9() { - let sm = init_source_map(); - let span = Span::with_root_ctxt(BytePos(12), BytePos(23)); - let sstr = sm.span_to_string(span); - - assert_eq!(sstr, "blork.rs:2:1: 2:12"); -} - -/// Tests failing to merge two spans on different lines. -#[test] -fn span_merging_fail() { - let sm = SourceMap::new(FilePathMapping::empty()); - let inputtext = "bbbb BB\ncc CCC\n"; - let selection1 = " ~~\n \n"; - let selection2 = " \n ~~~\n"; - sm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_owned()); - let span1 = span_from_selection(inputtext, selection1); - let span2 = span_from_selection(inputtext, selection2); - - assert!(sm.merge_spans(span1, span2).is_none()); -} - -/// Returns the span corresponding to the `n`th occurrence of `substring` in `source_text`. -trait SourceMapExtension { - fn span_substr( - &self, - file: &Lrc<SourceFile>, - source_text: &str, - substring: &str, - n: usize, - ) -> Span; -} - -impl SourceMapExtension for SourceMap { - fn span_substr( - &self, - file: &Lrc<SourceFile>, - 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::with_root_ctxt( - BytePos(lo as u32 + file.start_pos.0), - BytePos(hi as u32 + file.start_pos.0), - ); - assert_eq!(&self.span_to_snippet(span).unwrap()[..], substring); - return span; - } - i += 1; - } - } -} diff --git a/src/libsyntax_pos/span_encoding.rs b/src/libsyntax_pos/span_encoding.rs deleted file mode 100644 index d769cf83a03..00000000000 --- a/src/libsyntax_pos/span_encoding.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Spans are encoded using 1-bit tag and 2 different encoding formats (one for each tag value). -// One format is used for keeping span data inline, -// another contains index into an out-of-line span interner. -// The encoding format for inline spans were obtained by optimizing over crates in rustc/libstd. -// See https://internals.rust-lang.org/t/rfc-compiler-refactoring-spans/1357/28 - -use crate::hygiene::SyntaxContext; -use crate::GLOBALS; -use crate::{BytePos, SpanData}; - -use rustc_data_structures::fx::FxHashMap; - -/// A compressed span. -/// -/// `SpanData` is 12 bytes, which is a bit too big to stick everywhere. `Span` -/// is a form that only takes up 8 bytes, with less space for the length and -/// context. The vast majority (99.9%+) of `SpanData` instances will fit within -/// those 8 bytes; any `SpanData` whose fields don't fit into a `Span` are -/// stored in a separate interner table, and the `Span` will index into that -/// table. Interning is rare enough that the cost is low, but common enough -/// that the code is exercised regularly. -/// -/// An earlier version of this code used only 4 bytes for `Span`, but that was -/// slower because only 80--90% of spans could be stored inline (even less in -/// very large crates) and so the interner was used a lot more. -/// -/// Inline (compressed) format: -/// - `span.base_or_index == span_data.lo` -/// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`) -/// - `span.ctxt == span_data.ctxt` (must be `<= MAX_CTXT`) -/// -/// Interned format: -/// - `span.base_or_index == index` (indexes into the interner table) -/// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero) -/// - `span.ctxt == 0` -/// -/// The inline form uses 0 for the tag value (rather than 1) so that we don't -/// need to mask out the tag bit when getting the length, and so that the -/// dummy span can be all zeroes. -/// -/// Notes about the choice of field sizes: -/// - `base` is 32 bits in both `Span` and `SpanData`, which means that `base` -/// values never cause interning. The number of bits needed for `base` -/// depends on the crate size. 32 bits allows up to 4 GiB of code in a crate. -/// `script-servo` is the largest crate in `rustc-perf`, requiring 26 bits -/// for some spans. -/// - `len` is 15 bits in `Span` (a u16, minus 1 bit for the tag) and 32 bits -/// in `SpanData`, which means that large `len` values will cause interning. -/// The number of bits needed for `len` does not depend on the crate size. -/// The most common number of bits for `len` are 0--7, with a peak usually at -/// 3 or 4, and then it drops off quickly from 8 onwards. 15 bits is enough -/// for 99.99%+ of cases, but larger values (sometimes 20+ bits) might occur -/// dozens of times in a typical crate. -/// - `ctxt` is 16 bits in `Span` and 32 bits in `SpanData`, which means that -/// large `ctxt` values will cause interning. The number of bits needed for -/// `ctxt` values depend partly on the crate size and partly on the form of -/// the code. No crates in `rustc-perf` need more than 15 bits for `ctxt`, -/// but larger crates might need more than 16 bits. -/// -#[derive(Clone, Copy, Eq, PartialEq, Hash)] -pub struct Span { - base_or_index: u32, - len_or_tag: u16, - ctxt_or_zero: u16, -} - -const LEN_TAG: u16 = 0b1000_0000_0000_0000; -const MAX_LEN: u32 = 0b0111_1111_1111_1111; -const MAX_CTXT: u32 = 0b1111_1111_1111_1111; - -/// Dummy span, both position and length are zero, syntax context is zero as well. -pub const DUMMY_SP: Span = Span { base_or_index: 0, len_or_tag: 0, ctxt_or_zero: 0 }; - -impl Span { - #[inline] - pub fn new(mut lo: BytePos, mut hi: BytePos, ctxt: SyntaxContext) -> Self { - if lo > hi { - std::mem::swap(&mut lo, &mut hi); - } - - let (base, len, ctxt2) = (lo.0, hi.0 - lo.0, ctxt.as_u32()); - - if len <= MAX_LEN && ctxt2 <= MAX_CTXT { - // Inline format. - Span { base_or_index: base, len_or_tag: len as u16, ctxt_or_zero: ctxt2 as u16 } - } else { - // Interned format. - let index = with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt })); - Span { base_or_index: index, len_or_tag: LEN_TAG, ctxt_or_zero: 0 } - } - } - - #[inline] - pub fn data(self) -> SpanData { - if self.len_or_tag != LEN_TAG { - // Inline format. - debug_assert!(self.len_or_tag as u32 <= MAX_LEN); - SpanData { - lo: BytePos(self.base_or_index), - hi: BytePos(self.base_or_index + self.len_or_tag as u32), - ctxt: SyntaxContext::from_u32(self.ctxt_or_zero as u32), - } - } else { - // Interned format. - debug_assert!(self.ctxt_or_zero == 0); - let index = self.base_or_index; - with_span_interner(|interner| *interner.get(index)) - } - } -} - -#[derive(Default)] -pub struct SpanInterner { - spans: FxHashMap<SpanData, u32>, - span_data: Vec<SpanData>, -} - -impl SpanInterner { - fn intern(&mut self, span_data: &SpanData) -> u32 { - if let Some(index) = self.spans.get(span_data) { - return *index; - } - - let index = self.spans.len() as u32; - self.span_data.push(*span_data); - self.spans.insert(*span_data, index); - index - } - - #[inline] - fn get(&self, index: u32) -> &SpanData { - &self.span_data[index as usize] - } -} - -// If an interner exists, return it. Otherwise, prepare a fresh one. -#[inline] -fn with_span_interner<T, F: FnOnce(&mut SpanInterner) -> T>(f: F) -> T { - GLOBALS.with(|globals| f(&mut *globals.span_interner.lock())) -} diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs deleted file mode 100644 index 7ae037faf15..00000000000 --- a/src/libsyntax_pos/symbol.rs +++ /dev/null @@ -1,1213 +0,0 @@ -//! An "interner" is a data structure that associates values with usize tags and -//! allows bidirectional lookup; i.e., given a value, one can easily find the -//! type, and vice versa. - -use arena::DroplessArena; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; -use rustc_index::vec::Idx; -use rustc_macros::{symbols, HashStable_Generic}; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use rustc_serialize::{UseSpecializedDecodable, UseSpecializedEncodable}; - -use std::cmp::{Ord, PartialEq, PartialOrd}; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::str; - -use crate::{Span, DUMMY_SP, GLOBALS}; - -#[cfg(test)] -mod tests; - -symbols! { - // After modifying this list adjust `is_special`, `is_used_keyword`/`is_unused_keyword`, - // this should be rarely necessary though if the keywords are kept in alphabetic order. - Keywords { - // Special reserved identifiers used internally for elided lifetimes, - // unnamed method parameters, crate root module, error recovery etc. - Invalid: "", - PathRoot: "{{root}}", - DollarCrate: "$crate", - Underscore: "_", - - // Keywords that are used in stable Rust. - As: "as", - Break: "break", - Const: "const", - Continue: "continue", - Crate: "crate", - Else: "else", - Enum: "enum", - Extern: "extern", - False: "false", - Fn: "fn", - For: "for", - If: "if", - Impl: "impl", - In: "in", - Let: "let", - Loop: "loop", - Match: "match", - Mod: "mod", - Move: "move", - Mut: "mut", - Pub: "pub", - Ref: "ref", - Return: "return", - SelfLower: "self", - SelfUpper: "Self", - Static: "static", - Struct: "struct", - Super: "super", - Trait: "trait", - True: "true", - Type: "type", - Unsafe: "unsafe", - Use: "use", - Where: "where", - While: "while", - - // Keywords that are used in unstable Rust or reserved for future use. - Abstract: "abstract", - Become: "become", - Box: "box", - Do: "do", - Final: "final", - Macro: "macro", - Override: "override", - Priv: "priv", - Typeof: "typeof", - Unsized: "unsized", - Virtual: "virtual", - Yield: "yield", - - // Edition-specific keywords that are used in stable Rust. - Async: "async", // >= 2018 Edition only - Await: "await", // >= 2018 Edition only - Dyn: "dyn", // >= 2018 Edition only - - // Edition-specific keywords that are used in unstable Rust or reserved for future use. - Try: "try", // >= 2018 Edition only - - // Special lifetime names - UnderscoreLifetime: "'_", - StaticLifetime: "'static", - - // Weak keywords, have special meaning only in specific contexts. - Auto: "auto", - Catch: "catch", - Default: "default", - Raw: "raw", - Union: "union", - } - - // Symbols that can be referred to with syntax_pos::sym::*. The symbol is - // the stringified identifier unless otherwise specified (e.g. - // `proc_dash_macro` represents "proc-macro"). - // - // As well as the symbols listed, there are symbols for the the strings - // "0", "1", ..., "9", which are accessible via `sym::integer`. - Symbols { - aarch64_target_feature, - abi, - abi_amdgpu_kernel, - abi_efiapi, - abi_msp430_interrupt, - abi_ptx, - abi_sysv64, - abi_thiscall, - abi_unadjusted, - abi_vectorcall, - abi_x86_interrupt, - aborts, - add_with_overflow, - advanced_slice_patterns, - adx_target_feature, - alias, - align, - alignstack, - all, - allocator, - allocator_internals, - alloc_error_handler, - allow, - allowed, - allow_fail, - allow_internal_unsafe, - allow_internal_unstable, - allow_internal_unstable_backcompat_hack, - always, - and, - any, - arbitrary_enum_discriminant, - arbitrary_self_types, - Arguments, - ArgumentV1, - arm_target_feature, - asm, - assert, - associated_consts, - associated_type_bounds, - associated_type_defaults, - associated_types, - assume_init, - async_await, - async_closure, - attr, - attributes, - attr_literals, - augmented_assignments, - automatically_derived, - avx512_target_feature, - await_macro, - begin_panic, - bench, - bin, - bind_by_move_pattern_guards, - bindings_after_at, - block, - bool, - borrowck_graphviz_postflow, - borrowck_graphviz_preflow, - box_patterns, - box_syntax, - braced_empty_structs, - bswap, - bitreverse, - C, - caller_location, - cdylib, - cfg, - cfg_attr, - cfg_attr_multi, - cfg_doctest, - cfg_sanitize, - cfg_target_feature, - cfg_target_has_atomic, - cfg_target_thread_local, - cfg_target_vendor, - char, - clippy, - clone, - Clone, - clone_closures, - clone_from, - closure_to_fn_coercion, - cmp, - cmpxchg16b_target_feature, - cold, - column, - compile_error, - compiler_builtins, - concat, - concat_idents, - conservative_impl_trait, - console, - const_compare_raw_pointers, - const_constructor, - const_extern_fn, - const_fn, - const_fn_union, - const_generics, - const_if_match, - const_indexing, - const_in_array_repeat_expressions, - const_let, - const_loop, - const_mut_refs, - const_panic, - const_raw_ptr_deref, - const_raw_ptr_to_usize_cast, - const_transmute, - contents, - context, - convert, - Copy, - copy_closures, - core, - core_intrinsics, - crate_id, - crate_in_paths, - crate_local, - crate_name, - crate_type, - crate_visibility_modifier, - ctpop, - cttz, - cttz_nonzero, - ctlz, - ctlz_nonzero, - custom_attribute, - custom_derive, - custom_inner_attributes, - custom_test_frameworks, - c_variadic, - debug_trait, - declare_lint_pass, - decl_macro, - Debug, - Decodable, - Default, - default_lib_allocator, - default_type_parameter_fallback, - default_type_params, - delay_span_bug_from_inside_query, - deny, - deprecated, - deref, - deref_mut, - derive, - diagnostic, - direct, - doc, - doc_alias, - doc_cfg, - doc_keyword, - doc_masked, - doc_spotlight, - doctest, - document_private_items, - dotdoteq_in_patterns, - dotdot_in_tuple_patterns, - double_braced_crate: "{{crate}}", - double_braced_impl: "{{impl}}", - double_braced_misc: "{{misc}}", - double_braced_closure: "{{closure}}", - double_braced_constructor: "{{constructor}}", - double_braced_constant: "{{constant}}", - double_braced_opaque: "{{opaque}}", - dropck_eyepatch, - dropck_parametricity, - drop_types_in_const, - dylib, - dyn_trait, - eh_personality, - eh_unwind_resume, - enable, - Encodable, - env, - eq, - err, - Err, - Eq, - Equal, - enclosing_scope, - except, - exclusive_range_pattern, - exhaustive_integer_patterns, - exhaustive_patterns, - existential_type, - expected, - export_name, - expr, - extern_absolute_paths, - external_doc, - extern_crate_item_prelude, - extern_crate_self, - extern_in_paths, - extern_prelude, - extern_types, - f16c_target_feature, - f32, - f64, - feature, - ffi_returns_twice, - field, - field_init_shorthand, - file, - fmt, - fmt_internals, - fn_must_use, - forbid, - format_args, - format_args_nl, - from, - From, - from_desugaring, - from_error, - from_generator, - from_method, - from_ok, - from_usize, - fundamental, - future, - Future, - FxHashSet, - FxHashMap, - gen_future, - generators, - generic_associated_types, - generic_param_attrs, - global_allocator, - global_asm, - globs, - hash, - Hash, - HashSet, - HashMap, - hexagon_target_feature, - hidden, - homogeneous_aggregate, - html_favicon_url, - html_logo_url, - html_no_source, - html_playground_url, - html_root_url, - i128, - i128_type, - i16, - i32, - i64, - i8, - ident, - if_let, - if_while_or_patterns, - ignore, - impl_header_lifetime_elision, - impl_lint_pass, - impl_trait_in_bindings, - import_shadowing, - index, - index_mut, - in_band_lifetimes, - include, - include_bytes, - include_str, - inclusive_range_syntax, - infer_outlives_requirements, - infer_static_outlives_requirements, - inline, - intel, - into_future, - IntoFuture, - into_iter, - IntoIterator, - into_result, - intrinsics, - irrefutable_let_patterns, - isize, - issue, - issue_5723_bootstrap, - issue_tracker_base_url, - item, - item_context: "ItemContext", - item_like_imports, - iter, - Iterator, - keyword, - kind, - label, - label_break_value, - lang, - lang_items, - let_chains, - lhs, - lib, - lifetime, - line, - link, - linkage, - link_args, - link_cfg, - link_llvm_intrinsics, - link_name, - link_ordinal, - link_section, - LintPass, - lint_reasons, - literal, - local_inner_macros, - log_syntax, - loop_break_value, - macro_at_most_once_rep, - macro_escape, - macro_export, - macro_lifetime_matcher, - macro_literal_matcher, - macro_reexport, - macro_rules, - macros_in_extern, - macro_use, - macro_vis_matcher, - main, - managed_boxes, - marker, - marker_trait_attr, - masked, - match_beginning_vert, - match_default_bindings, - may_dangle, - maybe_uninit_uninit, - maybe_uninit_zeroed, - mem_uninitialized, - mem_zeroed, - member_constraints, - message, - meta, - min_align_of, - min_const_fn, - min_const_unsafe_fn, - mips_target_feature, - mmx_target_feature, - module, - module_path, - more_struct_aliases, - move_val_init, - movbe_target_feature, - mul_with_overflow, - must_use, - naked, - naked_functions, - name, - needs_allocator, - needs_drop, - needs_panic_runtime, - negate_unsigned, - never, - never_type, - never_type_fallback, - new, - next, - __next, - nll, - no_builtins, - no_core, - no_crate_inject, - no_debug, - no_default_passes, - no_implicit_prelude, - no_inline, - no_link, - no_main, - no_mangle, - non_ascii_idents, - None, - non_exhaustive, - non_modrs_mods, - no_stack_check, - no_start, - no_std, - not, - note, - object_safe_for_dispatch, - Ok, - omit_gdb_pretty_printer_section, - on, - on_unimplemented, - oom, - ops, - optimize, - optimize_attribute, - optin_builtin_traits, - option, - Option, - option_env, - opt_out_copy, - or, - or_patterns, - Ord, - Ordering, - Output, - overlapping_marker_traits, - packed, - panic, - panic_handler, - panic_impl, - panic_implementation, - panic_runtime, - parent_trait, - partial_cmp, - param_attrs, - PartialEq, - PartialOrd, - passes, - pat, - path, - pattern_parentheses, - Pending, - pin, - Pin, - pinned, - platform_intrinsics, - plugin, - plugin_registrar, - plugins, - Poll, - poll_with_tls_context, - powerpc_target_feature, - precise_pointer_size_matching, - pref_align_of, - prelude, - prelude_import, - primitive, - proc_dash_macro: "proc-macro", - proc_macro, - proc_macro_attribute, - proc_macro_def_site, - proc_macro_derive, - proc_macro_expr, - proc_macro_gen, - proc_macro_hygiene, - proc_macro_internals, - proc_macro_mod, - proc_macro_non_items, - proc_macro_path_invoc, - profiler_runtime, - ptr_offset_from, - pub_restricted, - pushpop_unsafe, - quad_precision_float, - question_mark, - quote, - Range, - RangeFrom, - RangeFull, - RangeInclusive, - RangeTo, - RangeToInclusive, - raw_dylib, - raw_identifiers, - raw_ref_op, - Ready, - reason, - recursion_limit, - reexport_test_harness_main, - reflect, - register_attr, - register_tool, - relaxed_adts, - repr, - repr128, - repr_align, - repr_align_enum, - repr_packed, - repr_simd, - repr_transparent, - re_rebalance_coherence, - result, - Result, - Return, - rhs, - rlib, - rotate_left, - rotate_right, - rt, - rtm_target_feature, - rust, - rust_2015_preview, - rust_2018_preview, - rust_begin_unwind, - rustc, - RustcDecodable, - RustcEncodable, - rustc_allocator, - rustc_allocator_nounwind, - rustc_allow_const_fn_ptr, - rustc_args_required_const, - rustc_attrs, - rustc_builtin_macro, - rustc_clean, - rustc_const_unstable, - rustc_const_stable, - rustc_conversion_suggestion, - rustc_def_path, - rustc_deprecated, - rustc_diagnostic_item, - rustc_diagnostic_macros, - rustc_dirty, - rustc_dummy, - rustc_dump_env_program_clauses, - rustc_dump_program_clauses, - rustc_dump_user_substs, - rustc_error, - rustc_expected_cgu_reuse, - rustc_if_this_changed, - rustc_inherit_overflow_checks, - rustc_layout, - rustc_layout_scalar_valid_range_end, - rustc_layout_scalar_valid_range_start, - rustc_macro_transparency, - rustc_mir, - rustc_nonnull_optimization_guaranteed, - rustc_object_lifetime_default, - rustc_on_unimplemented, - rustc_outlives, - rustc_paren_sugar, - rustc_partition_codegened, - rustc_partition_reused, - rustc_peek, - rustc_peek_definite_init, - rustc_peek_maybe_init, - rustc_peek_maybe_uninit, - rustc_peek_indirectly_mutable, - rustc_private, - rustc_proc_macro_decls, - rustc_promotable, - rustc_regions, - rustc_stable, - rustc_std_internal_symbol, - rustc_symbol_name, - rustc_synthetic, - rustc_reservation_impl, - rustc_test_marker, - rustc_then_this_would_need, - rustc_variance, - rustfmt, - rust_eh_personality, - rust_eh_unwind_resume, - rust_oom, - rvalue_static_promotion, - sanitize, - sanitizer_runtime, - saturating_add, - saturating_sub, - _Self, - self_in_typedefs, - self_struct_ctor, - send_trait, - should_panic, - simd, - simd_extract, - simd_ffi, - simd_insert, - since, - size, - size_of, - slice_patterns, - slicing_syntax, - soft, - Some, - specialization, - speed, - spotlight, - sse4a_target_feature, - stable, - staged_api, - start, - static_in_const, - staticlib, - static_nobundle, - static_recursion, - std, - std_inject, - str, - stringify, - stmt, - stmt_expr_attributes, - stop_after_dataflow, - struct_field_attributes, - struct_inherit, - structural_match, - struct_variant, - sty, - sub_with_overflow, - suggestion, - sync_trait, - target_feature, - target_has_atomic, - target_has_atomic_load_store, - target_thread_local, - task, - tbm_target_feature, - termination_trait, - termination_trait_test, - test, - test_2018_feature, - test_accepted_feature, - test_case, - test_removed_feature, - test_runner, - then_with, - thread_local, - tool_attributes, - tool_lints, - trace_macros, - track_caller, - trait_alias, - transmute, - transparent, - transparent_enums, - transparent_unions, - trivial_bounds, - Try, - try_blocks, - try_trait, - tt, - tuple_indexing, - Ty, - ty, - type_alias_impl_trait, - type_id, - type_name, - TyCtxt, - TyKind, - type_alias_enum_variants, - type_ascription, - type_length_limit, - type_macros, - u128, - u16, - u32, - u64, - u8, - unboxed_closures, - unchecked_shl, - unchecked_shr, - underscore_const_names, - underscore_imports, - underscore_lifetimes, - uniform_paths, - universal_impl_trait, - unmarked_api, - unreachable_code, - unrestricted_attribute_tokens, - unsafe_no_drop_flag, - unsized_locals, - unsized_tuple_coercion, - unstable, - untagged_unions, - unwind, - unwind_attributes, - unwrap_or, - used, - use_extern_macros, - use_nested_groups, - usize, - v1, - val, - var, - vec, - Vec, - vis, - visible_private_types, - volatile, - warn, - wasm_import_module, - wasm_target_feature, - while_let, - windows, - windows_subsystem, - wrapping_add, - wrapping_sub, - wrapping_mul, - Yield, - } -} - -#[derive(Copy, Clone, Eq, HashStable_Generic)] -pub struct Ident { - pub name: Symbol, - pub span: Span, -} - -impl Ident { - #[inline] - /// Constructs a new identifier from a symbol and a span. - pub const fn new(name: Symbol, span: Span) -> Ident { - Ident { name, span } - } - - /// Constructs a new identifier with a dummy span. - #[inline] - pub const fn with_dummy_span(name: Symbol) -> Ident { - Ident::new(name, DUMMY_SP) - } - - #[inline] - pub fn invalid() -> Ident { - Ident::with_dummy_span(kw::Invalid) - } - - /// Maps a string to an identifier with a dummy span. - pub fn from_str(string: &str) -> Ident { - Ident::with_dummy_span(Symbol::intern(string)) - } - - /// Maps a string and a span to an identifier. - pub fn from_str_and_span(string: &str, span: Span) -> Ident { - Ident::new(Symbol::intern(string), span) - } - - /// Replaces `lo` and `hi` with those from `span`, but keep hygiene context. - pub fn with_span_pos(self, span: Span) -> Ident { - Ident::new(self.name, span.with_ctxt(self.span.ctxt())) - } - - pub fn without_first_quote(self) -> Ident { - Ident::new(Symbol::intern(self.as_str().trim_start_matches('\'')), self.span) - } - - /// "Normalize" ident for use in comparisons using "item hygiene". - /// Identifiers with same string value become same if they came from the same "modern" macro - /// (e.g., `macro` item, but not `macro_rules` item) and stay different if they came from - /// different "modern" macros. - /// Technically, this operation strips all non-opaque marks from ident's syntactic context. - pub fn modern(self) -> Ident { - Ident::new(self.name, self.span.modern()) - } - - /// "Normalize" ident for use in comparisons using "local variable hygiene". - /// Identifiers with same string value become same if they came from the same non-transparent - /// macro (e.g., `macro` or `macro_rules!` items) and stay different if they came from different - /// non-transparent macros. - /// Technically, this operation strips all transparent marks from ident's syntactic context. - pub fn modern_and_legacy(self) -> Ident { - Ident::new(self.name, self.span.modern_and_legacy()) - } - - /// Convert the name to a `SymbolStr`. This is a slowish operation because - /// it requires locking the symbol interner. - pub fn as_str(self) -> SymbolStr { - self.name.as_str() - } -} - -impl PartialEq for Ident { - fn eq(&self, rhs: &Self) -> bool { - self.name == rhs.name && self.span.ctxt() == rhs.span.ctxt() - } -} - -impl Hash for Ident { - fn hash<H: Hasher>(&self, state: &mut H) { - self.name.hash(state); - self.span.ctxt().hash(state); - } -} - -impl fmt::Debug for Ident { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.is_raw_guess() { - write!(f, "r#")?; - } - write!(f, "{}{:?}", self.name, self.span.ctxt()) - } -} - -impl fmt::Display for Ident { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.is_raw_guess() { - write!(f, "r#")?; - } - fmt::Display::fmt(&self.name, f) - } -} - -impl UseSpecializedEncodable for Ident { - fn default_encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_struct("Ident", 2, |s| { - s.emit_struct_field("name", 0, |s| self.name.encode(s))?; - s.emit_struct_field("span", 1, |s| self.span.encode(s)) - }) - } -} - -impl UseSpecializedDecodable for Ident { - fn default_decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> { - d.read_struct("Ident", 2, |d| { - Ok(Ident { - name: d.read_struct_field("name", 0, Decodable::decode)?, - span: d.read_struct_field("span", 1, Decodable::decode)?, - }) - }) - } -} - -/// An interned string. -/// -/// Internally, a `Symbol` is implemented as an index, and all operations -/// (including hashing, equality, and ordering) operate on that index. The use -/// of `rustc_index::newtype_index!` means that `Option<Symbol>` only takes up 4 bytes, -/// because `rustc_index::newtype_index!` reserves the last 256 values for tagging purposes. -/// -/// Note that `Symbol` cannot directly be a `rustc_index::newtype_index!` because it -/// implements `fmt::Debug`, `Encodable`, and `Decodable` in special ways. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Symbol(SymbolIndex); - -rustc_index::newtype_index! { - pub struct SymbolIndex { .. } -} - -impl Symbol { - const fn new(n: u32) -> Self { - Symbol(SymbolIndex::from_u32_const(n)) - } - - /// Maps a string to its interned representation. - pub fn intern(string: &str) -> Self { - with_interner(|interner| interner.intern(string)) - } - - /// Access the symbol's chars. This is a slowish operation because it - /// requires locking the symbol interner. - pub fn with<F: FnOnce(&str) -> R, R>(self, f: F) -> R { - with_interner(|interner| f(interner.get(self))) - } - - /// Convert to a `SymbolStr`. This is a slowish operation because it - /// requires locking the symbol interner. - pub fn as_str(self) -> SymbolStr { - with_interner(|interner| unsafe { - SymbolStr { string: std::mem::transmute::<&str, &str>(interner.get(self)) } - }) - } - - pub fn as_u32(self) -> u32 { - self.0.as_u32() - } -} - -impl fmt::Debug for Symbol { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.with(|str| fmt::Debug::fmt(&str, f)) - } -} - -impl fmt::Display for Symbol { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.with(|str| fmt::Display::fmt(&str, f)) - } -} - -impl Encodable for Symbol { - fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { - self.with(|string| s.emit_str(string)) - } -} - -impl Decodable for Symbol { - fn decode<D: Decoder>(d: &mut D) -> Result<Symbol, D::Error> { - Ok(Symbol::intern(&d.read_str()?)) - } -} - -impl<CTX> HashStable<CTX> for Symbol { - #[inline] - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - self.as_str().hash_stable(hcx, hasher); - } -} - -impl<CTX> ToStableHashKey<CTX> for Symbol { - type KeyType = SymbolStr; - - #[inline] - fn to_stable_hash_key(&self, _: &CTX) -> SymbolStr { - self.as_str() - } -} - -// The `&'static str`s in this type actually point into the arena. -#[derive(Default)] -pub struct Interner { - arena: DroplessArena, - names: FxHashMap<&'static str, Symbol>, - strings: Vec<&'static str>, -} - -impl Interner { - fn prefill(init: &[&'static str]) -> Self { - Interner { - strings: init.into(), - names: init.iter().copied().zip((0..).map(Symbol::new)).collect(), - ..Default::default() - } - } - - pub fn intern(&mut self, string: &str) -> Symbol { - if let Some(&name) = self.names.get(string) { - return name; - } - - let name = Symbol::new(self.strings.len() as u32); - - // `from_utf8_unchecked` is safe since we just allocated a `&str` which is known to be - // UTF-8. - let string: &str = - unsafe { str::from_utf8_unchecked(self.arena.alloc_slice(string.as_bytes())) }; - // It is safe to extend the arena allocation to `'static` because we only access - // these while the arena is still alive. - let string: &'static str = unsafe { &*(string as *const str) }; - self.strings.push(string); - self.names.insert(string, name); - name - } - - // Get the symbol as a string. `Symbol::as_str()` should be used in - // preference to this function. - pub fn get(&self, symbol: Symbol) -> &str { - self.strings[symbol.0.as_usize()] - } -} - -// This module has a very short name because it's used a lot. -pub mod kw { - use super::Symbol; - keywords!(); -} - -// This module has a very short name because it's used a lot. -pub mod sym { - use super::Symbol; - use std::convert::TryInto; - - symbols!(); - - // Get the symbol for an integer. The first few non-negative integers each - // have a static symbol and therefore are fast. - pub fn integer<N: TryInto<usize> + Copy + ToString>(n: N) -> Symbol { - if let Result::Ok(idx) = n.try_into() { - if let Option::Some(&sym) = digits_array.get(idx) { - return sym; - } - } - Symbol::intern(&n.to_string()) - } -} - -impl Symbol { - fn is_used_keyword_2018(self) -> bool { - self >= kw::Async && self <= kw::Dyn - } - - fn is_unused_keyword_2018(self) -> bool { - self == kw::Try - } - - /// Used for sanity checking rustdoc keyword sections. - pub fn is_doc_keyword(self) -> bool { - self <= kw::Union - } - - /// A keyword or reserved identifier that can be used as a path segment. - pub fn is_path_segment_keyword(self) -> bool { - self == kw::Super - || self == kw::SelfLower - || self == kw::SelfUpper - || self == kw::Crate - || self == kw::PathRoot - || self == kw::DollarCrate - } - - /// Returns `true` if the symbol is `true` or `false`. - pub fn is_bool_lit(self) -> bool { - self == kw::True || self == kw::False - } - - /// This symbol can be a raw identifier. - pub fn can_be_raw(self) -> bool { - self != kw::Invalid && self != kw::Underscore && !self.is_path_segment_keyword() - } -} - -impl Ident { - // Returns `true` for reserved identifiers used internally for elided lifetimes, - // unnamed method parameters, crate root module, error recovery etc. - pub fn is_special(self) -> bool { - self.name <= kw::Underscore - } - - /// Returns `true` if the token is a keyword used in the language. - pub fn is_used_keyword(self) -> bool { - // Note: `span.edition()` is relatively expensive, don't call it unless necessary. - self.name >= kw::As && self.name <= kw::While - || self.name.is_used_keyword_2018() && self.span.rust_2018() - } - - /// Returns `true` if the token is a keyword reserved for possible future use. - pub fn is_unused_keyword(self) -> bool { - // Note: `span.edition()` is relatively expensive, don't call it unless necessary. - self.name >= kw::Abstract && self.name <= kw::Yield - || self.name.is_unused_keyword_2018() && self.span.rust_2018() - } - - /// Returns `true` if the token is either a special identifier or a keyword. - pub fn is_reserved(self) -> bool { - self.is_special() || self.is_used_keyword() || self.is_unused_keyword() - } - - /// A keyword or reserved identifier that can be used as a path segment. - pub fn is_path_segment_keyword(self) -> bool { - self.name.is_path_segment_keyword() - } - - /// We see this identifier in a normal identifier position, like variable name or a type. - /// How was it written originally? Did it use the raw form? Let's try to guess. - pub fn is_raw_guess(self) -> bool { - self.name.can_be_raw() && self.is_reserved() - } -} - -#[inline] -fn with_interner<T, F: FnOnce(&mut Interner) -> T>(f: F) -> T { - GLOBALS.with(|globals| f(&mut *globals.symbol_interner.lock())) -} - -/// An alternative to `Symbol`, useful when the chars within the symbol need to -/// be accessed. It deliberately has limited functionality and should only be -/// used for temporary values. -/// -/// Because the interner outlives any thread which uses this type, we can -/// safely treat `string` which points to interner data, as an immortal string, -/// as long as this type never crosses between threads. -// -// FIXME: ensure that the interner outlives any thread which uses `SymbolStr`, -// by creating a new thread right after constructing the interner. -#[derive(Clone, Eq, PartialOrd, Ord)] -pub struct SymbolStr { - string: &'static str, -} - -// This impl allows a `SymbolStr` to be directly equated with a `String` or -// `&str`. -impl<T: std::ops::Deref<Target = str>> std::cmp::PartialEq<T> for SymbolStr { - fn eq(&self, other: &T) -> bool { - self.string == other.deref() - } -} - -impl !Send for SymbolStr {} -impl !Sync for SymbolStr {} - -/// This impl means that if `ss` is a `SymbolStr`: -/// - `*ss` is a `str`; -/// - `&*ss` is a `&str`; -/// - `&ss as &str` is a `&str`, which means that `&ss` can be passed to a -/// function expecting a `&str`. -impl std::ops::Deref for SymbolStr { - type Target = str; - #[inline] - fn deref(&self) -> &str { - self.string - } -} - -impl fmt::Debug for SymbolStr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.string, f) - } -} - -impl fmt::Display for SymbolStr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.string, f) - } -} - -impl<CTX> HashStable<CTX> for SymbolStr { - #[inline] - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - self.string.hash_stable(hcx, hasher) - } -} - -impl<CTX> ToStableHashKey<CTX> for SymbolStr { - type KeyType = SymbolStr; - - #[inline] - fn to_stable_hash_key(&self, _: &CTX) -> SymbolStr { - self.clone() - } -} diff --git a/src/libsyntax_pos/symbol/tests.rs b/src/libsyntax_pos/symbol/tests.rs deleted file mode 100644 index f74b9a0cd1d..00000000000 --- a/src/libsyntax_pos/symbol/tests.rs +++ /dev/null @@ -1,25 +0,0 @@ -use super::*; - -use crate::{edition, Globals}; - -#[test] -fn interner_tests() { - let mut i: Interner = Interner::default(); - // first one is zero: - assert_eq!(i.intern("dog"), Symbol::new(0)); - // re-use gets the same entry: - assert_eq!(i.intern("dog"), Symbol::new(0)); - // different string gets a different #: - assert_eq!(i.intern("cat"), Symbol::new(1)); - assert_eq!(i.intern("cat"), Symbol::new(1)); - // dog is still at zero - assert_eq!(i.intern("dog"), Symbol::new(0)); -} - -#[test] -fn without_first_quote_test() { - GLOBALS.set(&Globals::new(edition::DEFAULT_EDITION), || { - let i = Ident::from_str("'break"); - assert_eq!(i.without_first_quote().name, kw::Break); - }); -} diff --git a/src/libsyntax_pos/tests.rs b/src/libsyntax_pos/tests.rs deleted file mode 100644 index 3c8eb8bcd31..00000000000 --- a/src/libsyntax_pos/tests.rs +++ /dev/null @@ -1,40 +0,0 @@ -use super::*; - -#[test] -fn test_lookup_line() { - let lines = &[BytePos(3), BytePos(17), BytePos(28)]; - - assert_eq!(lookup_line(lines, BytePos(0)), -1); - assert_eq!(lookup_line(lines, BytePos(3)), 0); - assert_eq!(lookup_line(lines, BytePos(4)), 0); - - assert_eq!(lookup_line(lines, BytePos(16)), 0); - assert_eq!(lookup_line(lines, BytePos(17)), 1); - assert_eq!(lookup_line(lines, BytePos(18)), 1); - - assert_eq!(lookup_line(lines, BytePos(28)), 2); - assert_eq!(lookup_line(lines, BytePos(29)), 2); -} - -#[test] -fn test_normalize_newlines() { - fn check(before: &str, after: &str, expected_positions: &[u32]) { - let mut actual = before.to_string(); - let mut actual_positions = vec![]; - normalize_newlines(&mut actual, &mut actual_positions); - let actual_positions: Vec<_> = actual_positions.into_iter().map(|nc| nc.pos.0).collect(); - assert_eq!(actual.as_str(), after); - assert_eq!(actual_positions, expected_positions); - } - check("", "", &[]); - check("\n", "\n", &[]); - check("\r", "\r", &[]); - check("\r\r", "\r\r", &[]); - check("\r\n", "\n", &[1]); - check("hello world", "hello world", &[]); - check("hello\nworld", "hello\nworld", &[]); - check("hello\r\nworld", "hello\nworld", &[6]); - check("\r\nhello\r\nworld\r\n", "\nhello\nworld\n", &[1, 7, 13]); - check("\r\r\n", "\r\n", &[2]); - check("hello\rworld", "hello\rworld", &[]); -} |
