// SPDX-License-Identifier: Apache-2.0 OR MIT #[cfg(wrap_proc_macro)]use crate::imp;#[cfg(span_locations)]use crate::location::LineColumn;use crate::parse::{self, Cursor};use crate::rcvec::{RcVec, RcVecBuilder, RcVecIntoIter, RcVecMut};use crate::{Delimiter, Spacing, TokenTree};#[cfg(all(span_locations, not(fuzzing)))]use alloc::collections::BTreeMap;#[cfg(all(span_locations, not(fuzzing)))]use core::cell::RefCell;#[cfg(span_locations)]use core::cmp;#[cfg(all(span_locations, not(fuzzing)))]use core::cmp::Ordering;use core::fmt::{self, Debug, Display, Write};use core::mem::ManuallyDrop;#[cfg(span_locations)]use core::ops::Range;use core::ops::RangeBounds;use core::ptr;use core::str;#[cfg(feature = "proc-macro")]use core::str::FromStr;use std::ffi::CStr;#[cfg(wrap_proc_macro)]use std::panic;#[cfg(span_locations)]use std::path::PathBuf; /// Force use of proc-macro2's fallback implementation of the API for now, even/// if the compiler's implementation is available.pub fn force() { #[cfg(wrap_proc_macro)] crate::detection::force_fallback();} /// Resume using the compiler's implementation of the proc macro API if it is/// available.pub fn unforce() { #[cfg(wrap_proc_macro)] crate::detection::unforce_fallback();} #[derive(Clone)]pub(crate) struct TokenStream { inner: RcVec<TokenTree>,} #[derive(Debug)]pub(crate) struct LexError { pub(crate) span: Span,} impl LexError { pub(crate) fn span(&self) -> Span { self.span } pub(crate) fn call_site() -> Self { LexError { span: Span::call_site(), } }} impl TokenStream { pub(crate) fn new() -> Self { TokenStream { inner: RcVecBuilder::new().build(), } } pub(crate) fn from_str_checked(src: &str) -> Result<Self, LexError> { // Create a dummy file & add it to the source map let mut cursor = get_cursor(src); // Strip a byte order mark if present const BYTE_ORDER_MARK: &str = "\u{feff}"; if cursor.starts_with(BYTE_ORDER_MARK) { cursor = cursor.advance(BYTE_ORDER_MARK.len()); } parse::token_stream(cursor) } #[cfg(feature = "proc-macro")] pub(crate) fn from_str_unchecked(src: &str) -> Self { Self::from_str_checked(src).unwrap() } pub(crate) fn is_empty(&self) -> bool { self.inner.len() == 0 } fn take_inner(self) -> RcVecBuilder<TokenTree> { let nodrop = ManuallyDrop::new(self); unsafe { ptr::read(&nodrop.inner) }.make_owned() }} fn push_token_from_proc_macro(mut vec: RcVecMut<TokenTree>, token: TokenTree) { // https://github.com/dtolnay/proc-macro2/issues/235 match token { TokenTree::Literal(crate::Literal { #[cfg(wrap_proc_macro)] inner: crate::imp::Literal::Fallback(literal), #[cfg(not(wrap_proc_macro))] inner: literal, .. }) if literal.repr.starts_with('-') => { push_negative_literal(vec, literal); } _ => vec.push(token), } #[cold] fn push_negative_literal(mut vec: RcVecMut<TokenTree>, mut literal: Literal) { literal.repr.remove(0); let mut punct = crate::Punct::new('-', Spacing::Alone); punct.set_span(crate::Span::_new_fallback(literal.span)); vec.push(TokenTree::Punct(punct)); vec.push(TokenTree::Literal(crate::Literal::_new_fallback(literal))); }} // Nonrecursive to prevent stack overflow.impl Drop for TokenStream { fn drop(&mut self) { let mut stack = Vec::new(); let mut current = match self.inner.get_mut() { Some(inner) => inner.take().into_iter(), None => return, }; loop { while let Some(token) = current.next() { let group = match token { TokenTree::Group(group) => group.inner, _ => continue, }; #[cfg(wrap_proc_macro)] let group = match group { crate::imp::Group::Fallback(group) => group, crate::imp::Group::Compiler(_) => continue, }; let mut group = group; if let Some(inner) = group.stream.inner.get_mut() { stack.push(current); current = inner.take().into_iter(); } } match stack.pop() { Some(next) => current = next, None => return, } } }} pub(crate) struct TokenStreamBuilder { inner: RcVecBuilder<TokenTree>,} impl TokenStreamBuilder { pub(crate) fn new() -> Self { TokenStreamBuilder { inner: RcVecBuilder::new(), } } pub(crate) fn with_capacity(cap: usize) -> Self { TokenStreamBuilder { inner: RcVecBuilder::with_capacity(cap), } } pub(crate) fn push_token_from_parser(&mut self, tt: TokenTree) { self.inner.push(tt); } pub(crate) fn build(self) -> TokenStream { TokenStream { inner: self.inner.build(), } }} #[cfg(span_locations)]fn get_cursor(src: &str) -> Cursor { #[cfg(fuzzing)] return Cursor { rest: src, off: 1 }; // Create a dummy file & add it to the source map #[cfg(not(fuzzing))] SOURCE_MAP.with(|sm| { let mut sm = sm.borrow_mut(); let span = sm.add_file(src); Cursor { rest: src, off: span.lo, } })} #[cfg(not(span_locations))]fn get_cursor(src: &str) -> Cursor { Cursor { rest: src }} impl Display for LexError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("cannot parse string into token stream") }} impl Display for TokenStream { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut joint = false; for (i, tt) in self.inner.iter().enumerate() { if i != 0 && !joint { write!(f, " ")?; } joint = false; match tt { TokenTree::Group(tt) => Display::fmt(tt, f), TokenTree::Ident(tt) => Display::fmt(tt, f), TokenTree::Punct(tt) => { joint = tt.spacing() == Spacing::Joint; Display::fmt(tt, f) } TokenTree::Literal(tt) => Display::fmt(tt, f), }?; } Ok(()) }} impl Debug for TokenStream { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("TokenStream ")?; f.debug_list().entries(self.clone()).finish() }} #[cfg(feature = "proc-macro")]impl From<proc_macro::TokenStream> for TokenStream { fn from(inner: proc_macro::TokenStream) -> Self { TokenStream::from_str_unchecked(&inner.to_string()) }} #[cfg(feature = "proc-macro")]impl From<TokenStream> for proc_macro::TokenStream { fn from(inner: TokenStream) -> Self { proc_macro::TokenStream::from_str_unchecked(&inner.to_string()) }} impl From<TokenTree> for TokenStream { fn from(tree: TokenTree) -> Self { let mut stream = RcVecBuilder::new(); push_token_from_proc_macro(stream.as_mut(), tree); TokenStream { inner: stream.build(), } }} impl FromIterator<TokenTree> for TokenStream { fn from_iter<I: IntoIterator<Item = TokenTree>>(tokens: I) -> Self { let mut stream = TokenStream::new(); stream.extend(tokens); stream }} impl FromIterator<TokenStream> for TokenStream { fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self { let mut v = RcVecBuilder::new(); for stream in streams { v.extend(stream.take_inner()); } TokenStream { inner: v.build() } }} impl Extend<TokenTree> for TokenStream { fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, tokens: I) { let mut vec = self.inner.make_mut(); tokens .into_iter() .for_each(|token| push_token_from_proc_macro(vec.as_mut(), token)); }} impl Extend<TokenStream> for TokenStream { fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) { self.inner.make_mut().extend(streams.into_iter().flatten()); }} pub(crate) type TokenTreeIter = RcVecIntoIter<TokenTree>; impl IntoIterator for TokenStream { type Item = TokenTree; type IntoIter = TokenTreeIter; fn into_iter(self) -> TokenTreeIter { self.take_inner().into_iter() }} #[cfg(all(span_locations, not(fuzzing)))]thread_local! { static SOURCE_MAP: RefCell<SourceMap> = RefCell::new(SourceMap { // Start with a single dummy file which all call_site() and def_site() // spans reference. files: vec![FileInfo { source_text: String::new(), span: Span { lo: 0, hi: 0 }, lines: vec![0], char_index_to_byte_offset: BTreeMap::new(), }], });} #[cfg(span_locations)]pub(crate) fn invalidate_current_thread_spans() { #[cfg(not(fuzzing))] SOURCE_MAP.with(|sm| sm.borrow_mut().files.truncate(1));} #[cfg(all(span_locations, not(fuzzing)))]struct FileInfo { source_text: String, span: Span, lines: Vec<usize>, char_index_to_byte_offset: BTreeMap<usize, usize>,} #[cfg(all(span_locations, not(fuzzing)))]impl FileInfo { fn offset_line_column(&self, offset: usize) -> LineColumn { assert!(self.span_within(Span { lo: offset as u32, hi: offset as u32, })); let offset = offset - self.span.lo as usize; match self.lines.binary_search(&offset) { Ok(found) => LineColumn { line: found + 1, column: 0, }, Err(idx) => LineColumn { line: idx, column: offset - self.lines[idx - 1], }, } } fn span_within(&self, span: Span) -> bool { span.lo >= self.span.lo && span.hi <= self.span.hi } fn byte_range(&mut self, span: Span) -> Range<usize> { let lo_char = (span.lo - self.span.lo) as usize; // Look up offset of the largest already-computed char index that is // less than or equal to the current requested one. We resume counting // chars from that point. let (&last_char_index, &last_byte_offset) = self .char_index_to_byte_offset .range(..=lo_char) .next_back() .unwrap_or((&0, &0)); let lo_byte = if last_char_index == lo_char { last_byte_offset } else { let total_byte_offset = match self.source_text[last_byte_offset..] .char_indices() .nth(lo_char - last_char_index) { Some((additional_offset, _ch)) => last_byte_offset + additional_offset, None => self.source_text.len(), }; self.char_index_to_byte_offset .insert(lo_char, total_byte_offset); total_byte_offset }; let trunc_lo = &self.source_text[lo_byte..]; let char_len = (span.hi - span.lo) as usize; lo_byte..match trunc_lo.char_indices().nth(char_len) { Some((offset, _ch)) => lo_byte + offset, None => self.source_text.len(), } } fn source_text(&mut self, span: Span) -> String { let byte_range = self.byte_range(span); self.source_text[byte_range].to_owned() }} /// Computes the offsets of each line in the given source string/// and the total number of characters#[cfg(all(span_locations, not(fuzzing)))]fn lines_offsets(s: &str) -> (usize, Vec<usize>) { let mut lines = vec![0]; let mut total = 0; for ch in s.chars() { total += 1; if ch == '\n' { lines.push(total); } } (total, lines)} #[cfg(all(span_locations, not(fuzzing)))]struct SourceMap { files: Vec<FileInfo>,} #[cfg(all(span_locations, not(fuzzing)))]impl SourceMap { fn next_start_pos(&self) -> u32 { // Add 1 so there's always space between files. // // We'll always have at least 1 file, as we initialize our files list // with a dummy file. self.files.last().unwrap().span.hi + 1 } fn add_file(&mut self, src: &str) -> Span { let (len, lines) = lines_offsets(src); let lo = self.next_start_pos(); let span = Span { lo, hi: lo + (len as u32), }; self.files.push(FileInfo { source_text: src.to_owned(), span, lines, // Populated lazily by source_text(). char_index_to_byte_offset: BTreeMap::new(), }); span } fn find(&self, span: Span) -> usize { match self.files.binary_search_by(|file| { if file.span.hi < span.lo { Ordering::Less } else if file.span.lo > span.hi { Ordering::Greater } else { assert!(file.span_within(span)); Ordering::Equal } }) { Ok(i) => i, Err(_) => unreachable!("Invalid span with no related FileInfo!"), } } fn filepath(&self, span: Span) -> String { let i = self.find(span); if i == 0 { "<unspecified>".to_owned() } else { format!("<parsed string {}>", i) } } fn fileinfo(&self, span: Span) -> &FileInfo { let i = self.find(span); &self.files[i] } fn fileinfo_mut(&mut self, span: Span) -> &mut FileInfo { let i = self.find(span); &mut self.files[i] }} #[derive(Clone, Copy, PartialEq, Eq)]pub(crate) struct Span { #[cfg(span_locations)]Lines 1–500 of 1258. Load more
// SPDX-License-Identifier: Apache-2.0 OR MIT #[cfg(wrap_proc_macro)] use crate::imp; #[cfg(span_locations)] use crate::location::LineColumn; use crate::parse::{self, Cursor}; use crate::rcvec::{RcVec, RcVecBuilder, RcVecIntoIter, RcVecMut}; use crate::{Delimiter, Spacing, TokenTree}; #[cfg(all(span_locations, not(fuzzing)))] use alloc::collections::BTreeMap; #[cfg(all(span_locations, not(fuzzing)))] use core::cell::RefCell; #[cfg(span_locations)] use core::cmp; #[cfg(all(span_locations, not(fuzzing)))] use core::cmp::Ordering; use core::fmt::{self, Debug, Display, Write}; use core::mem::ManuallyDrop; #[cfg(span_locations)] use core::ops::Range; use core::ops::RangeBounds; use core::ptr; use core::str; #[cfg(feature = "proc-macro")] use core::str::FromStr; use std::ffi::CStr; #[cfg(wrap_proc_macro)] use std::panic; #[cfg(span_locations)] use std::path::PathBuf; /// Force use of proc-macro2's fallback implementation of the API for now, even /// if the compiler's implementation is available. pub fn force() { #[cfg(wrap_proc_macro)] crate::detection::force_fallback(); } /// Resume using the compiler's implementation of the proc macro API if it is /// available. pub fn unforce() { #[cfg(wrap_proc_macro)] crate::detection::unforce_fallback(); } #[derive(Clone)] pub(crate) struct TokenStream { inner: RcVec<TokenTree>, } #[derive(Debug)] pub(crate) struct LexError { pub(crate) span: Span, } impl LexError { pub(crate) fn span(&self) -> Span { self.span } pub(crate) fn call_site() -> Self { LexError { span: Span::call_site(), } } } impl TokenStream { pub(crate) fn new() -> Self { TokenStream { inner: RcVecBuilder::new().build(), } } pub(crate) fn from_str_checked(src: &str) -> Result<Self, LexError> { // Create a dummy file & add it to the source map let mut cursor = get_cursor(src); // Strip a byte order mark if present const BYTE_ORDER_MARK: &str = "\u{feff}"; if cursor.starts_with(BYTE_ORDER_MARK) { cursor = cursor.advance(BYTE_ORDER_MARK.len()); } parse::token_stream(cursor) } #[cfg(feature = "proc-macro")] pub(crate) fn from_str_unchecked(src: &str) -> Self { Self::from_str_checked(src).unwrap() } pub(crate) fn is_empty(&self) -> bool { self.inner.len() == 0 } fn take_inner(self) -> RcVecBuilder<TokenTree> { let nodrop = ManuallyDrop::new(self); unsafe { ptr::read(&nodrop.inner) }.make_owned() } } fn push_token_from_proc_macro(mut vec: RcVecMut<TokenTree>, token: TokenTree) { // https://github.com/dtolnay/proc-macro2/issues/235 match token { TokenTree::Literal(crate::Literal { #[cfg(wrap_proc_macro)] inner: crate::imp::Literal::Fallback(literal), #[cfg(not(wrap_proc_macro))] inner: literal, .. }) if literal.repr.starts_with('-') => { push_negative_literal(vec, literal); } _ => vec.push(token), } #[cold] fn push_negative_literal(mut vec: RcVecMut<TokenTree>, mut literal: Literal) { literal.repr.remove(0); let mut punct = crate::Punct::new('-', Spacing::Alone); punct.set_span(crate::Span::_new_fallback(literal.span)); vec.push(TokenTree::Punct(punct)); vec.push(TokenTree::Literal(crate::Literal::_new_fallback(literal))); } } // Nonrecursive to prevent stack overflow. impl Drop for TokenStream { fn drop(&mut self) { let mut stack = Vec::new(); let mut current = match self.inner.get_mut() { Some(inner) => inner.take().into_iter(), None => return, }; loop { while let Some(token) = current.next() { let group = match token { TokenTree::Group(group) => group.inner, _ => continue, }; #[cfg(wrap_proc_macro)] let group = match group { crate::imp::Group::Fallback(group) => group, crate::imp::Group::Compiler(_) => continue, }; let mut group = group; if let Some(inner) = group.stream.inner.get_mut() { stack.push(current); current = inner.take().into_iter(); } } match stack.pop() { Some(next) => current = next, None => return, } } } } pub(crate) struct TokenStreamBuilder { inner: RcVecBuilder<TokenTree>, } impl TokenStreamBuilder { pub(crate) fn new() -> Self { TokenStreamBuilder { inner: RcVecBuilder::new(), } } pub(crate) fn with_capacity(cap: usize) -> Self { TokenStreamBuilder { inner: RcVecBuilder::with_capacity(cap), } } pub(crate) fn push_token_from_parser(&mut self, tt: TokenTree) { self.inner.push(tt); } pub(crate) fn build(self) -> TokenStream { TokenStream { inner: self.inner.build(), } } } #[cfg(span_locations)] fn get_cursor(src: &str) -> Cursor { #[cfg(fuzzing)] return Cursor { rest: src, off: 1 }; // Create a dummy file & add it to the source map #[cfg(not(fuzzing))] SOURCE_MAP.with(|sm| { let mut sm = sm.borrow_mut(); let span = sm.add_file(src); Cursor { rest: src, off: span.lo, } }) } #[cfg(not(span_locations))] fn get_cursor(src: &str) -> Cursor { Cursor { rest: src } } impl Display for LexError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("cannot parse string into token stream") } } impl Display for TokenStream { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut joint = false; for (i, tt) in self.inner.iter().enumerate() { if i != 0 && !joint { write!(f, " ")?; } joint = false; match tt { TokenTree::Group(tt) => Display::fmt(tt, f), TokenTree::Ident(tt) => Display::fmt(tt, f), TokenTree::Punct(tt) => { joint = tt.spacing() == Spacing::Joint; Display::fmt(tt, f) } TokenTree::Literal(tt) => Display::fmt(tt, f), }?; } Ok(()) } } impl Debug for TokenStream { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("TokenStream ")?; f.debug_list().entries(self.clone()).finish() } } #[cfg(feature = "proc-macro")] impl From<proc_macro::TokenStream> for TokenStream { fn from(inner: proc_macro::TokenStream) -> Self { TokenStream::from_str_unchecked(&inner.to_string()) } } #[cfg(feature = "proc-macro")] impl From<TokenStream> for proc_macro::TokenStream { fn from(inner: TokenStream) -> Self { proc_macro::TokenStream::from_str_unchecked(&inner.to_string()) } } impl From<TokenTree> for TokenStream { fn from(tree: TokenTree) -> Self { let mut stream = RcVecBuilder::new(); push_token_from_proc_macro(stream.as_mut(), tree); TokenStream { inner: stream.build(), } } } impl FromIterator<TokenTree> for TokenStream { fn from_iter<I: IntoIterator<Item = TokenTree>>(tokens: I) -> Self { let mut stream = TokenStream::new(); stream.extend(tokens); stream } } impl FromIterator<TokenStream> for TokenStream { fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self { let mut v = RcVecBuilder::new(); for stream in streams { v.extend(stream.take_inner()); } TokenStream { inner: v.build() } } } impl Extend<TokenTree> for TokenStream { fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, tokens: I) { let mut vec = self.inner.make_mut(); tokens .into_iter() .for_each(|token| push_token_from_proc_macro(vec.as_mut(), token)); } } impl Extend<TokenStream> for TokenStream { fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) { self.inner.make_mut().extend(streams.into_iter().flatten()); } } pub(crate) type TokenTreeIter = RcVecIntoIter<TokenTree>; impl IntoIterator for TokenStream { type Item = TokenTree; type IntoIter = TokenTreeIter; fn into_iter(self) -> TokenTreeIter { self.take_inner().into_iter() } } #[cfg(all(span_locations, not(fuzzing)))] thread_local! { static SOURCE_MAP: RefCell<SourceMap> = RefCell::new(SourceMap { // Start with a single dummy file which all call_site() and def_site() // spans reference. files: vec![FileInfo { source_text: String::new(), span: Span { lo: 0, hi: 0 }, lines: vec![0], char_index_to_byte_offset: BTreeMap::new(), }], }); } #[cfg(span_locations)] pub(crate) fn invalidate_current_thread_spans() { #[cfg(not(fuzzing))] SOURCE_MAP.with(|sm| sm.borrow_mut().files.truncate(1)); } #[cfg(all(span_locations, not(fuzzing)))] struct FileInfo { source_text: String, span: Span, lines: Vec<usize>, char_index_to_byte_offset: BTreeMap<usize, usize>, } #[cfg(all(span_locations, not(fuzzing)))] impl FileInfo { fn offset_line_column(&self, offset: usize) -> LineColumn { assert!(self.span_within(Span { lo: offset as u32, hi: offset as u32, })); let offset = offset - self.span.lo as usize; match self.lines.binary_search(&offset) { Ok(found) => LineColumn { line: found + 1, column: 0, }, Err(idx) => LineColumn { line: idx, column: offset - self.lines[idx - 1], }, } } fn span_within(&self, span: Span) -> bool { span.lo >= self.span.lo && span.hi <= self.span.hi } fn byte_range(&mut self, span: Span) -> Range<usize> { let lo_char = (span.lo - self.span.lo) as usize; // Look up offset of the largest already-computed char index that is // less than or equal to the current requested one. We resume counting // chars from that point. let (&last_char_index, &last_byte_offset) = self .char_index_to_byte_offset .range(..=lo_char) .next_back() .unwrap_or((&0, &0)); let lo_byte = if last_char_index == lo_char { last_byte_offset } else { let total_byte_offset = match self.source_text[last_byte_offset..] .char_indices() .nth(lo_char - last_char_index) { Some((additional_offset, _ch)) => last_byte_offset + additional_offset, None => self.source_text.len(), }; self.char_index_to_byte_offset .insert(lo_char, total_byte_offset); total_byte_offset }; let trunc_lo = &self.source_text[lo_byte..]; let char_len = (span.hi - span.lo) as usize; lo_byte..match trunc_lo.char_indices().nth(char_len) { Some((offset, _ch)) => lo_byte + offset, None => self.source_text.len(), } } fn source_text(&mut self, span: Span) -> String { let byte_range = self.byte_range(span); self.source_text[byte_range].to_owned() } } /// Computes the offsets of each line in the given source string /// and the total number of characters #[cfg(all(span_locations, not(fuzzing)))] fn lines_offsets(s: &str) -> (usize, Vec<usize>) { let mut lines = vec![0]; let mut total = 0; for ch in s.chars() { total += 1; if ch == '\n' { lines.push(total); } } (total, lines) } #[cfg(all(span_locations, not(fuzzing)))] struct SourceMap { files: Vec<FileInfo>, } #[cfg(all(span_locations, not(fuzzing)))] impl SourceMap { fn next_start_pos(&self) -> u32 { // Add 1 so there's always space between files. // // We'll always have at least 1 file, as we initialize our files list // with a dummy file. self.files.last().unwrap().span.hi + 1 } fn add_file(&mut self, src: &str) -> Span { let (len, lines) = lines_offsets(src); let lo = self.next_start_pos(); let span = Span { lo, hi: lo + (len as u32), }; self.files.push(FileInfo { source_text: src.to_owned(), span, lines, // Populated lazily by source_text(). char_index_to_byte_offset: BTreeMap::new(), }); span } fn find(&self, span: Span) -> usize { match self.files.binary_search_by(|file| { if file.span.hi < span.lo { Ordering::Less } else if file.span.lo > span.hi { Ordering::Greater } else { assert!(file.span_within(span)); Ordering::Equal } }) { Ok(i) => i, Err(_) => unreachable!("Invalid span with no related FileInfo!"), } } fn filepath(&self, span: Span) -> String { let i = self.find(span); if i == 0 { "<unspecified>".to_owned() } else { format!("<parsed string {}>", i) } } fn fileinfo(&self, span: Span) -> &FileInfo { let i = self.find(span); &self.files[i] } fn fileinfo_mut(&mut self, span: Span) -> &mut FileInfo { let i = self.find(span); &mut self.files[i] } } #[derive(Clone, Copy, PartialEq, Eq)] pub(crate) struct Span { #[cfg(span_locations)]