1use std::io::{self, BorrowedBuf, Read};
13use std::{fs, path};
14
15use rustc_data_structures::sync::{IntoDynSyncSend, MappedReadGuard, ReadGuard, RwLock};
16use rustc_data_structures::unhash::UnhashMap;
17use rustc_macros::{Decodable, Encodable};
18use tracing::{debug, instrument, trace};
19
20use crate::*;
21
22#[cfg(test)]
23mod tests;
24
25pub fn original_sp(sp: Span, enclosing_sp: Span) -> Span {
29 let ctxt = sp.ctxt();
30 if ctxt.is_root() {
31 return sp;
32 }
33
34 let enclosing_ctxt = enclosing_sp.ctxt();
35 let expn_data1 = ctxt.outer_expn_data();
36 if !enclosing_ctxt.is_root()
37 && expn_data1.call_site == enclosing_ctxt.outer_expn_data().call_site
38 {
39 sp
40 } else {
41 original_sp(expn_data1.call_site, enclosing_sp)
42 }
43}
44
45mod monotonic {
46 use std::ops::{Deref, DerefMut};
47
48 pub struct MonotonicVec<T>(Vec<T>);
54 impl<T> MonotonicVec<T> {
55 pub(super) fn push(&mut self, val: T) {
56 self.0.push(val);
57 }
58 }
59
60 impl<T> Default for MonotonicVec<T> {
61 fn default() -> Self {
62 MonotonicVec(vec![])
63 }
64 }
65
66 impl<T> Deref for MonotonicVec<T> {
67 type Target = Vec<T>;
68 fn deref(&self) -> &Self::Target {
69 &self.0
70 }
71 }
72
73 impl<T> !DerefMut for MonotonicVec<T> {}
74}
75
76#[derive(Clone, Encodable, Decodable, Debug, Copy, PartialEq, Hash, HashStable_Generic)]
77pub struct Spanned<T> {
78 pub node: T,
79 pub span: Span,
80}
81
82pub fn respan<T>(sp: Span, t: T) -> Spanned<T> {
83 Spanned { node: t, span: sp }
84}
85
86pub fn dummy_spanned<T>(t: T) -> Spanned<T> {
87 respan(DUMMY_SP, t)
88}
89
90pub trait FileLoader {
96 fn file_exists(&self, path: &Path) -> bool;
98
99 fn read_file(&self, path: &Path) -> io::Result<String>;
103
104 fn read_binary_file(&self, path: &Path) -> io::Result<Arc<[u8]>>;
107}
108
109pub struct RealFileLoader;
111
112impl FileLoader for RealFileLoader {
113 fn file_exists(&self, path: &Path) -> bool {
114 path.exists()
115 }
116
117 fn read_file(&self, path: &Path) -> io::Result<String> {
118 if path.metadata().is_ok_and(|metadata| metadata.len() > SourceFile::MAX_FILE_SIZE.into()) {
119 return Err(io::Error::other(format!(
120 "text files larger than {} bytes are unsupported",
121 SourceFile::MAX_FILE_SIZE
122 )));
123 }
124 fs::read_to_string(path)
125 }
126
127 fn read_binary_file(&self, path: &Path) -> io::Result<Arc<[u8]>> {
128 let mut file = fs::File::open(path)?;
129 let len = file.metadata()?.len();
130
131 let mut bytes = Arc::new_uninit_slice(len as usize);
132 let mut buf = BorrowedBuf::from(Arc::get_mut(&mut bytes).unwrap());
133 match file.read_buf_exact(buf.unfilled()) {
134 Ok(()) => {}
135 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
136 drop(bytes);
137 return fs::read(path).map(Vec::into);
138 }
139 Err(e) => return Err(e),
140 }
141 let bytes = unsafe { bytes.assume_init() };
144
145 let mut probe = [0u8; 32];
155 let n = loop {
156 match file.read(&mut probe) {
157 Ok(0) => return Ok(bytes),
158 Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
159 Err(e) => return Err(e),
160 Ok(n) => break n,
161 }
162 };
163 let mut bytes: Vec<u8> = bytes.iter().copied().chain(probe[..n].iter().copied()).collect();
164 file.read_to_end(&mut bytes)?;
165 Ok(bytes.into())
166 }
167}
168
169#[derive(Default)]
174struct SourceMapFiles {
175 source_files: monotonic::MonotonicVec<Arc<SourceFile>>,
176 stable_id_to_source_file: UnhashMap<StableSourceFileId, Arc<SourceFile>>,
177}
178
179pub struct SourceMapInputs {
181 pub file_loader: Box<dyn FileLoader + Send + Sync>,
182 pub path_mapping: FilePathMapping,
183 pub hash_kind: SourceFileHashAlgorithm,
184 pub checksum_hash_kind: Option<SourceFileHashAlgorithm>,
185}
186
187pub struct SourceMap {
188 files: RwLock<SourceMapFiles>,
189 file_loader: IntoDynSyncSend<Box<dyn FileLoader + Sync + Send>>,
190
191 path_mapping: FilePathMapping,
194
195 hash_kind: SourceFileHashAlgorithm,
197
198 checksum_hash_kind: Option<SourceFileHashAlgorithm>,
203}
204
205impl SourceMap {
206 pub fn new(path_mapping: FilePathMapping) -> SourceMap {
207 Self::with_inputs(SourceMapInputs {
208 file_loader: Box::new(RealFileLoader),
209 path_mapping,
210 hash_kind: SourceFileHashAlgorithm::Md5,
211 checksum_hash_kind: None,
212 })
213 }
214
215 pub fn with_inputs(
216 SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }: SourceMapInputs,
217 ) -> SourceMap {
218 SourceMap {
219 files: Default::default(),
220 file_loader: IntoDynSyncSend(file_loader),
221 path_mapping,
222 hash_kind,
223 checksum_hash_kind,
224 }
225 }
226
227 pub fn path_mapping(&self) -> &FilePathMapping {
228 &self.path_mapping
229 }
230
231 pub fn file_exists(&self, path: &Path) -> bool {
232 self.file_loader.file_exists(path)
233 }
234
235 pub fn load_file(&self, path: &Path) -> io::Result<Arc<SourceFile>> {
236 let src = self.file_loader.read_file(path)?;
237 let filename = path.to_owned().into();
238 Ok(self.new_source_file(filename, src))
239 }
240
241 pub fn load_binary_file(&self, path: &Path) -> io::Result<(Arc<[u8]>, Span)> {
246 let bytes = self.file_loader.read_binary_file(path)?;
247
248 let text = std::str::from_utf8(&bytes).unwrap_or("").to_string();
254 let file = self.new_source_file(path.to_owned().into(), text);
255 Ok((
256 bytes,
257 Span::new(
258 file.start_pos,
259 BytePos(file.start_pos.0 + file.source_len.0),
260 SyntaxContext::root(),
261 None,
262 ),
263 ))
264 }
265
266 pub fn files(&self) -> MappedReadGuard<'_, monotonic::MonotonicVec<Arc<SourceFile>>> {
269 ReadGuard::map(self.files.borrow(), |files| &files.source_files)
270 }
271
272 pub fn source_file_by_stable_id(
273 &self,
274 stable_id: StableSourceFileId,
275 ) -> Option<Arc<SourceFile>> {
276 self.files.borrow().stable_id_to_source_file.get(&stable_id).cloned()
277 }
278
279 fn register_source_file(
280 &self,
281 file_id: StableSourceFileId,
282 mut file: SourceFile,
283 ) -> Result<Arc<SourceFile>, OffsetOverflowError> {
284 let mut files = self.files.borrow_mut();
285
286 file.start_pos = BytePos(if let Some(last_file) = files.source_files.last() {
287 last_file.end_position().0.checked_add(1).ok_or(OffsetOverflowError)?
290 } else {
291 0
292 });
293
294 let file = Arc::new(file);
295 files.source_files.push(Arc::clone(&file));
296 files.stable_id_to_source_file.insert(file_id, Arc::clone(&file));
297
298 Ok(file)
299 }
300
301 pub fn new_source_file(&self, filename: FileName, src: String) -> Arc<SourceFile> {
305 self.try_new_source_file(filename, src).unwrap_or_else(|OffsetOverflowError| {
306 eprintln!(
307 "fatal error: rustc does not support text files larger than {} bytes",
308 SourceFile::MAX_FILE_SIZE
309 );
310 crate::fatal_error::FatalError.raise()
311 })
312 }
313
314 fn try_new_source_file(
315 &self,
316 filename: FileName,
317 src: String,
318 ) -> Result<Arc<SourceFile>, OffsetOverflowError> {
319 let (filename, _) = self.path_mapping.map_filename_prefix(&filename);
323
324 let stable_id = StableSourceFileId::from_filename_in_current_crate(&filename);
325 match self.source_file_by_stable_id(stable_id) {
326 Some(lrc_sf) => Ok(lrc_sf),
327 None => {
328 let source_file =
329 SourceFile::new(filename, src, self.hash_kind, self.checksum_hash_kind)?;
330
331 debug_assert_eq!(source_file.stable_id, stable_id);
334
335 self.register_source_file(stable_id, source_file)
336 }
337 }
338 }
339
340 pub fn new_imported_source_file(
345 &self,
346 filename: FileName,
347 src_hash: SourceFileHash,
348 checksum_hash: Option<SourceFileHash>,
349 stable_id: StableSourceFileId,
350 source_len: u32,
351 cnum: CrateNum,
352 file_local_lines: FreezeLock<SourceFileLines>,
353 multibyte_chars: Vec<MultiByteChar>,
354 normalized_pos: Vec<NormalizedPos>,
355 metadata_index: u32,
356 ) -> Arc<SourceFile> {
357 let source_len = RelativeBytePos::from_u32(source_len);
358
359 let source_file = SourceFile {
360 name: filename,
361 src: None,
362 src_hash,
363 checksum_hash,
364 external_src: FreezeLock::new(ExternalSource::Foreign {
365 kind: ExternalSourceKind::AbsentOk,
366 metadata_index,
367 }),
368 start_pos: BytePos(0),
369 source_len,
370 lines: file_local_lines,
371 multibyte_chars,
372 normalized_pos,
373 stable_id,
374 cnum,
375 };
376
377 self.register_source_file(stable_id, source_file)
378 .expect("not enough address space for imported source file")
379 }
380
381 pub fn doctest_offset_line(&self, file: &FileName, orig: usize) -> usize {
383 match file {
384 FileName::DocTest(_, offset) => {
385 if *offset < 0 {
386 orig - (-(*offset)) as usize
387 } else {
388 orig + *offset as usize
389 }
390 }
391 _ => orig,
392 }
393 }
394
395 pub fn lookup_source_file(&self, pos: BytePos) -> Arc<SourceFile> {
397 let idx = self.lookup_source_file_idx(pos);
398 Arc::clone(&(*self.files.borrow().source_files)[idx])
399 }
400
401 pub fn lookup_char_pos(&self, pos: BytePos) -> Loc {
403 let sf = self.lookup_source_file(pos);
404 let (line, col, col_display) = sf.lookup_file_pos_with_col_display(pos);
405 Loc { file: sf, line, col, col_display }
406 }
407
408 pub fn lookup_line(&self, pos: BytePos) -> Result<SourceFileAndLine, Arc<SourceFile>> {
410 let f = self.lookup_source_file(pos);
411
412 let pos = f.relative_position(pos);
413 match f.lookup_line(pos) {
414 Some(line) => Ok(SourceFileAndLine { sf: f, line }),
415 None => Err(f),
416 }
417 }
418
419 pub fn span_to_string(
420 &self,
421 sp: Span,
422 filename_display_pref: FileNameDisplayPreference,
423 ) -> String {
424 let (source_file, lo_line, lo_col, hi_line, hi_col) = self.span_to_location_info(sp);
425
426 let file_name = match source_file {
427 Some(sf) => sf.name.display(filename_display_pref).to_string(),
428 None => return "no-location".to_string(),
429 };
430
431 format!(
432 "{file_name}:{lo_line}:{lo_col}{}",
433 if let FileNameDisplayPreference::Short = filename_display_pref {
434 String::new()
435 } else {
436 format!(": {hi_line}:{hi_col}")
437 }
438 )
439 }
440
441 pub fn span_to_location_info(
442 &self,
443 sp: Span,
444 ) -> (Option<Arc<SourceFile>>, usize, usize, usize, usize) {
445 if self.files.borrow().source_files.is_empty() || sp.is_dummy() {
446 return (None, 0, 0, 0, 0);
447 }
448
449 let lo = self.lookup_char_pos(sp.lo());
450 let hi = self.lookup_char_pos(sp.hi());
451 (Some(lo.file), lo.line, lo.col.to_usize() + 1, hi.line, hi.col.to_usize() + 1)
452 }
453
454 pub fn span_to_embeddable_string(&self, sp: Span) -> String {
456 self.span_to_string(sp, FileNameDisplayPreference::Remapped)
457 }
458
459 pub fn span_to_diagnostic_string(&self, sp: Span) -> String {
463 self.span_to_string(sp, self.path_mapping.filename_display_for_diagnostics)
464 }
465
466 pub fn span_to_filename(&self, sp: Span) -> FileName {
467 self.lookup_char_pos(sp.lo()).file.name.clone()
468 }
469
470 pub fn filename_for_diagnostics<'a>(&self, filename: &'a FileName) -> FileNameDisplay<'a> {
471 filename.display(self.path_mapping.filename_display_for_diagnostics)
472 }
473
474 pub fn is_multiline(&self, sp: Span) -> bool {
475 let lo = self.lookup_source_file_idx(sp.lo());
476 let hi = self.lookup_source_file_idx(sp.hi());
477 if lo != hi {
478 return true;
479 }
480 let f = Arc::clone(&(*self.files.borrow().source_files)[lo]);
481 let lo = f.relative_position(sp.lo());
482 let hi = f.relative_position(sp.hi());
483 f.lookup_line(lo) != f.lookup_line(hi)
484 }
485
486 #[instrument(skip(self), level = "trace")]
487 pub fn is_valid_span(&self, sp: Span) -> Result<(Loc, Loc), SpanLinesError> {
488 let lo = self.lookup_char_pos(sp.lo());
489 trace!(?lo);
490 let hi = self.lookup_char_pos(sp.hi());
491 trace!(?hi);
492 if lo.file.start_pos != hi.file.start_pos {
493 return Err(SpanLinesError::DistinctSources(Box::new(DistinctSources {
494 begin: (lo.file.name.clone(), lo.file.start_pos),
495 end: (hi.file.name.clone(), hi.file.start_pos),
496 })));
497 }
498 Ok((lo, hi))
499 }
500
501 pub fn is_line_before_span_empty(&self, sp: Span) -> bool {
502 match self.span_to_prev_source(sp) {
503 Ok(s) => s.rsplit_once('\n').unwrap_or(("", &s)).1.trim_start().is_empty(),
504 Err(_) => false,
505 }
506 }
507
508 pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
509 debug!("span_to_lines(sp={:?})", sp);
510 let (lo, hi) = self.is_valid_span(sp)?;
511 assert!(hi.line >= lo.line);
512
513 if sp.is_dummy() {
514 return Ok(FileLines { file: lo.file, lines: Vec::new() });
515 }
516
517 let mut lines = Vec::with_capacity(hi.line - lo.line + 1);
518
519 let mut start_col = lo.col;
522
523 let hi_line = hi.line.saturating_sub(1);
531 for line_index in lo.line.saturating_sub(1)..hi_line {
532 let line_len = lo.file.get_line(line_index).map_or(0, |s| s.chars().count());
533 lines.push(LineInfo { line_index, start_col, end_col: CharPos::from_usize(line_len) });
534 start_col = CharPos::from_usize(0);
535 }
536
537 lines.push(LineInfo { line_index: hi_line, start_col, end_col: hi.col });
539
540 Ok(FileLines { file: lo.file, lines })
541 }
542
543 pub fn span_to_source<F, T>(&self, sp: Span, extract_source: F) -> Result<T, SpanSnippetError>
547 where
548 F: Fn(&str, usize, usize) -> Result<T, SpanSnippetError>,
549 {
550 let local_begin = self.lookup_byte_offset(sp.lo());
551 let local_end = self.lookup_byte_offset(sp.hi());
552
553 if local_begin.sf.start_pos != local_end.sf.start_pos {
554 Err(SpanSnippetError::DistinctSources(Box::new(DistinctSources {
555 begin: (local_begin.sf.name.clone(), local_begin.sf.start_pos),
556 end: (local_end.sf.name.clone(), local_end.sf.start_pos),
557 })))
558 } else {
559 self.ensure_source_file_source_present(&local_begin.sf);
560
561 let start_index = local_begin.pos.to_usize();
562 let end_index = local_end.pos.to_usize();
563 let source_len = local_begin.sf.source_len.to_usize();
564
565 if start_index > end_index || end_index > source_len {
566 return Err(SpanSnippetError::MalformedForSourcemap(MalformedSourceMapPositions {
567 name: local_begin.sf.name.clone(),
568 source_len,
569 begin_pos: local_begin.pos,
570 end_pos: local_end.pos,
571 }));
572 }
573
574 if let Some(ref src) = local_begin.sf.src {
575 extract_source(src, start_index, end_index)
576 } else if let Some(src) = local_begin.sf.external_src.read().get_source() {
577 extract_source(src, start_index, end_index)
578 } else {
579 Err(SpanSnippetError::SourceNotAvailable { filename: local_begin.sf.name.clone() })
580 }
581 }
582 }
583
584 pub fn is_span_accessible(&self, sp: Span) -> bool {
585 self.span_to_source(sp, |src, start_index, end_index| {
586 Ok(src.get(start_index..end_index).is_some())
587 })
588 .is_ok_and(|is_accessible| is_accessible)
589 }
590
591 pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
593 self.span_to_source(sp, |src, start_index, end_index| {
594 src.get(start_index..end_index)
595 .map(|s| s.to_string())
596 .ok_or(SpanSnippetError::IllFormedSpan(sp))
597 })
598 }
599
600 pub fn span_to_margin(&self, sp: Span) -> Option<usize> {
601 Some(self.indentation_before(sp)?.len())
602 }
603
604 pub fn indentation_before(&self, sp: Span) -> Option<String> {
605 self.span_to_source(sp, |src, start_index, _| {
606 let before = &src[..start_index];
607 let last_line = before.rsplit_once('\n').map_or(before, |(_, last)| last);
608 Ok(last_line
609 .split_once(|c: char| !c.is_whitespace())
610 .map_or(last_line, |(indent, _)| indent)
611 .to_string())
612 })
613 .ok()
614 }
615
616 pub fn span_to_prev_source(&self, sp: Span) -> Result<String, SpanSnippetError> {
618 self.span_to_source(sp, |src, start_index, _| {
619 src.get(..start_index).map(|s| s.to_string()).ok_or(SpanSnippetError::IllFormedSpan(sp))
620 })
621 }
622
623 pub fn span_extend_to_prev_char(&self, sp: Span, c: char, accept_newlines: bool) -> Span {
626 if let Ok(prev_source) = self.span_to_prev_source(sp) {
627 let prev_source = prev_source.rsplit(c).next().unwrap_or("");
628 if !prev_source.is_empty() && (accept_newlines || !prev_source.contains('\n')) {
629 return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
630 }
631 }
632
633 sp
634 }
635
636 pub fn span_extend_to_prev_str(
640 &self,
641 sp: Span,
642 pat: &str,
643 accept_newlines: bool,
644 include_whitespace: bool,
645 ) -> Option<Span> {
646 let prev_source = self.span_to_prev_source(sp).ok()?;
651 for ws in &[" ", "\t", "\n"] {
652 let pat = pat.to_owned() + ws;
653 if let Some(pat_pos) = prev_source.rfind(&pat) {
654 let just_after_pat_pos = pat_pos + pat.len() - 1;
655 let just_after_pat_plus_ws = if include_whitespace {
656 just_after_pat_pos
657 + prev_source[just_after_pat_pos..]
658 .find(|c: char| !c.is_whitespace())
659 .unwrap_or(0)
660 } else {
661 just_after_pat_pos
662 };
663 let len = prev_source.len() - just_after_pat_plus_ws;
664 let prev_source = &prev_source[just_after_pat_plus_ws..];
665 if accept_newlines || !prev_source.trim_start().contains('\n') {
666 return Some(sp.with_lo(BytePos(sp.lo().0 - len as u32)));
667 }
668 }
669 }
670
671 None
672 }
673
674 pub fn span_to_next_source(&self, sp: Span) -> Result<String, SpanSnippetError> {
676 self.span_to_source(sp, |src, _, end_index| {
677 src.get(end_index..).map(|s| s.to_string()).ok_or(SpanSnippetError::IllFormedSpan(sp))
678 })
679 }
680
681 pub fn span_extend_while(
683 &self,
684 span: Span,
685 f: impl Fn(char) -> bool,
686 ) -> Result<Span, SpanSnippetError> {
687 self.span_to_source(span, |s, _start, end| {
688 let n = s[end..].char_indices().find(|&(_, c)| !f(c)).map_or(s.len() - end, |(i, _)| i);
689 Ok(span.with_hi(span.hi() + BytePos(n as u32)))
690 })
691 }
692
693 pub fn span_extend_while_whitespace(&self, span: Span) -> Span {
696 self.span_extend_while(span, char::is_whitespace).unwrap_or(span)
697 }
698
699 pub fn span_extend_prev_while(
701 &self,
702 span: Span,
703 f: impl Fn(char) -> bool,
704 ) -> Result<Span, SpanSnippetError> {
705 self.span_to_source(span, |s, start, _end| {
706 let n = s[..start]
707 .char_indices()
708 .rfind(|&(_, c)| !f(c))
709 .map_or(start, |(i, _)| start - i - 1);
710 Ok(span.with_lo(span.lo() - BytePos(n as u32)))
711 })
712 }
713
714 pub fn span_extend_to_next_char(&self, sp: Span, c: char, accept_newlines: bool) -> Span {
716 if let Ok(next_source) = self.span_to_next_source(sp) {
717 let next_source = next_source.split(c).next().unwrap_or("");
718 if !next_source.is_empty() && (accept_newlines || !next_source.contains('\n')) {
719 return sp.with_hi(BytePos(sp.hi().0 + next_source.len() as u32));
720 }
721 }
722
723 sp
724 }
725
726 pub fn span_extend_to_line(&self, sp: Span) -> Span {
728 self.span_extend_to_prev_char(self.span_extend_to_next_char(sp, '\n', true), '\n', true)
729 }
730
731 pub fn span_until_char(&self, sp: Span, c: char) -> Span {
734 match self.span_to_snippet(sp) {
735 Ok(snippet) => {
736 let snippet = snippet.split(c).next().unwrap_or("").trim_end();
737 if !snippet.is_empty() && !snippet.contains('\n') {
738 sp.with_hi(BytePos(sp.lo().0 + snippet.len() as u32))
739 } else {
740 sp
741 }
742 }
743 _ => sp,
744 }
745 }
746
747 pub fn span_wrapped_by_angle_or_parentheses(&self, span: Span) -> bool {
752 self.span_to_source(span, |src, start_index, end_index| {
753 if src.get(start_index..end_index).is_none() {
754 return Ok(false);
755 }
756 let end_src = &src[end_index..];
758 let mut i = 0;
759 let mut found_right_parentheses = false;
760 let mut found_right_angle = false;
761 while let Some(cc) = end_src.chars().nth(i) {
762 if cc == ' ' {
763 i = i + 1;
764 } else if cc == '>' {
765 found_right_angle = true;
767 break;
768 } else if cc == ')' {
769 found_right_parentheses = true;
770 break;
771 } else {
772 return Ok(false);
774 }
775 }
776 i = start_index;
778 let start_src = &src[0..start_index];
779 while let Some(cc) = start_src.chars().nth(i) {
780 if cc == ' ' {
781 if i == 0 {
782 return Ok(false);
783 }
784 i = i - 1;
785 } else if cc == '<' {
786 if !found_right_angle {
788 return Ok(false);
790 }
791 break;
792 } else if cc == '(' {
793 if !found_right_parentheses {
794 return Ok(false);
796 }
797 break;
798 } else {
799 return Ok(false);
801 }
802 }
803 Ok(true)
804 })
805 .is_ok_and(|is_accessible| is_accessible)
806 }
807
808 pub fn span_through_char(&self, sp: Span, c: char) -> Span {
811 if let Ok(snippet) = self.span_to_snippet(sp) {
812 if let Some(offset) = snippet.find(c) {
813 return sp.with_hi(BytePos(sp.lo().0 + (offset + c.len_utf8()) as u32));
814 }
815 }
816 sp
817 }
818
819 pub fn span_until_non_whitespace(&self, sp: Span) -> Span {
824 let mut whitespace_found = false;
825
826 self.span_take_while(sp, |c| {
827 if !whitespace_found && c.is_whitespace() {
828 whitespace_found = true;
829 }
830
831 !whitespace_found || c.is_whitespace()
832 })
833 }
834
835 pub fn span_until_whitespace(&self, sp: Span) -> Span {
840 self.span_take_while(sp, |c| !c.is_whitespace())
841 }
842
843 pub fn span_take_while<P>(&self, sp: Span, predicate: P) -> Span
845 where
846 P: for<'r> FnMut(&'r char) -> bool,
847 {
848 if let Ok(snippet) = self.span_to_snippet(sp) {
849 let offset = snippet.chars().take_while(predicate).map(|c| c.len_utf8()).sum::<usize>();
850
851 sp.with_hi(BytePos(sp.lo().0 + (offset as u32)))
852 } else {
853 sp
854 }
855 }
856
857 pub fn guess_head_span(&self, sp: Span) -> Span {
863 self.span_until_char(sp, '{')
866 }
867
868 pub fn start_point(&self, sp: Span) -> Span {
870 let width = {
871 let sp = sp.data();
872 let local_begin = self.lookup_byte_offset(sp.lo);
873 let start_index = local_begin.pos.to_usize();
874 let src = local_begin.sf.external_src.read();
875
876 let snippet = if let Some(ref src) = local_begin.sf.src {
877 Some(&src[start_index..])
878 } else {
879 src.get_source().map(|src| &src[start_index..])
880 };
881
882 match snippet {
883 None => 1,
884 Some(snippet) => match snippet.chars().next() {
885 None => 1,
886 Some(c) => c.len_utf8(),
887 },
888 }
889 };
890
891 sp.with_hi(BytePos(sp.lo().0 + width as u32))
892 }
893
894 pub fn end_point(&self, sp: Span) -> Span {
896 let pos = sp.hi().0;
897
898 let width = self.find_width_of_character_at_span(sp, false);
899 let corrected_end_position = pos.checked_sub(width).unwrap_or(pos);
900
901 let end_point = BytePos(cmp::max(corrected_end_position, sp.lo().0));
902 sp.with_lo(end_point)
903 }
904
905 pub fn next_point(&self, sp: Span) -> Span {
912 if sp.is_dummy() {
913 return sp;
914 }
915 let start_of_next_point = sp.hi().0;
916
917 let width = self.find_width_of_character_at_span(sp, true);
918 let end_of_next_point =
922 start_of_next_point.checked_add(width).unwrap_or(start_of_next_point);
923
924 let end_of_next_point = BytePos(cmp::max(start_of_next_point + 1, end_of_next_point));
925 Span::new(BytePos(start_of_next_point), end_of_next_point, sp.ctxt(), None)
926 }
927
928 pub fn span_look_ahead(&self, span: Span, expect: &str, limit: Option<usize>) -> Option<Span> {
930 let mut sp = span;
931 for _ in 0..limit.unwrap_or(100_usize) {
932 sp = self.next_point(sp);
933 if let Ok(ref snippet) = self.span_to_snippet(sp) {
934 if snippet == expect {
935 return Some(sp);
936 }
937 if snippet.chars().any(|c| !c.is_whitespace()) {
938 break;
939 }
940 }
941 }
942 None
943 }
944
945 #[instrument(skip(self, sp))]
948 fn find_width_of_character_at_span(&self, sp: Span, forwards: bool) -> u32 {
949 let sp = sp.data();
950
951 if sp.lo == sp.hi && !forwards {
952 debug!("early return empty span");
953 return 1;
954 }
955
956 let local_begin = self.lookup_byte_offset(sp.lo);
957 let local_end = self.lookup_byte_offset(sp.hi);
958 debug!("local_begin=`{:?}`, local_end=`{:?}`", local_begin, local_end);
959
960 if local_begin.sf.start_pos != local_end.sf.start_pos {
961 debug!("begin and end are in different files");
962 return 1;
963 }
964
965 let start_index = local_begin.pos.to_usize();
966 let end_index = local_end.pos.to_usize();
967 debug!("start_index=`{:?}`, end_index=`{:?}`", start_index, end_index);
968
969 if (!forwards && end_index == usize::MIN) || (forwards && start_index == usize::MAX) {
972 debug!("start or end of span, cannot be multibyte");
973 return 1;
974 }
975
976 let source_len = local_begin.sf.source_len.to_usize();
977 debug!("source_len=`{:?}`", source_len);
978 if start_index > end_index || end_index > source_len - 1 {
980 debug!("source indexes are malformed");
981 return 1;
982 }
983
984 let src = local_begin.sf.external_src.read();
985
986 let snippet = if let Some(src) = &local_begin.sf.src {
987 src
988 } else if let Some(src) = src.get_source() {
989 src
990 } else {
991 return 1;
992 };
993
994 if forwards {
995 (snippet.ceil_char_boundary(end_index + 1) - end_index) as u32
996 } else {
997 (end_index - snippet.floor_char_boundary(end_index - 1)) as u32
998 }
999 }
1000
1001 pub fn get_source_file(&self, filename: &FileName) -> Option<Arc<SourceFile>> {
1002 let filename = self.path_mapping().map_filename_prefix(filename).0;
1004 for sf in self.files.borrow().source_files.iter() {
1005 if filename == sf.name {
1006 return Some(Arc::clone(&sf));
1007 }
1008 }
1009 None
1010 }
1011
1012 pub fn lookup_byte_offset(&self, bpos: BytePos) -> SourceFileAndBytePos {
1014 let idx = self.lookup_source_file_idx(bpos);
1015 let sf = Arc::clone(&(*self.files.borrow().source_files)[idx]);
1016 let offset = bpos - sf.start_pos;
1017 SourceFileAndBytePos { sf, pos: offset }
1018 }
1019
1020 pub fn lookup_source_file_idx(&self, pos: BytePos) -> usize {
1024 self.files.borrow().source_files.partition_point(|x| x.start_pos <= pos) - 1
1025 }
1026
1027 pub fn count_lines(&self) -> usize {
1028 self.files().iter().fold(0, |a, f| a + f.count_lines())
1029 }
1030
1031 pub fn ensure_source_file_source_present(&self, source_file: &SourceFile) -> bool {
1032 source_file.add_external_src(|| {
1033 let FileName::Real(ref name) = source_file.name else {
1034 return None;
1035 };
1036
1037 let local_path: Cow<'_, Path> = match name {
1038 RealFileName::LocalPath(local_path) => local_path.into(),
1039 RealFileName::Remapped { local_path: Some(local_path), .. } => local_path.into(),
1040 RealFileName::Remapped { local_path: None, virtual_name } => {
1041 self.path_mapping.reverse_map_prefix_heuristically(virtual_name)?.into()
1047 }
1048 };
1049
1050 self.file_loader.read_file(&local_path).ok()
1051 })
1052 }
1053
1054 pub fn is_imported(&self, sp: Span) -> bool {
1055 let source_file_index = self.lookup_source_file_idx(sp.lo());
1056 let source_file = &self.files()[source_file_index];
1057 source_file.is_imported()
1058 }
1059
1060 pub fn stmt_span(&self, stmt_span: Span, block_span: Span) -> Span {
1064 if !stmt_span.from_expansion() {
1065 return stmt_span;
1066 }
1067 let mac_call = original_sp(stmt_span, block_span);
1068 self.mac_call_stmt_semi_span(mac_call).map_or(mac_call, |s| mac_call.with_hi(s.hi()))
1069 }
1070
1071 pub fn mac_call_stmt_semi_span(&self, mac_call: Span) -> Option<Span> {
1079 let span = self.span_extend_while_whitespace(mac_call);
1080 let span = self.next_point(span);
1081 if self.span_to_snippet(span).as_deref() == Ok(";") { Some(span) } else { None }
1082 }
1083}
1084
1085pub fn get_source_map() -> Option<Arc<SourceMap>> {
1086 with_session_globals(|session_globals| session_globals.source_map.clone())
1087}
1088
1089#[derive(Clone)]
1090pub struct FilePathMapping {
1091 mapping: Vec<(PathBuf, PathBuf)>,
1092 filename_display_for_diagnostics: FileNameDisplayPreference,
1093}
1094
1095impl FilePathMapping {
1096 pub fn empty() -> FilePathMapping {
1097 FilePathMapping::new(Vec::new(), FileNameDisplayPreference::Local)
1098 }
1099
1100 pub fn new(
1101 mapping: Vec<(PathBuf, PathBuf)>,
1102 filename_display_for_diagnostics: FileNameDisplayPreference,
1103 ) -> FilePathMapping {
1104 FilePathMapping { mapping, filename_display_for_diagnostics }
1105 }
1106
1107 pub fn map_prefix<'a>(&'a self, path: impl Into<Cow<'a, Path>>) -> (Cow<'a, Path>, bool) {
1111 let path = path.into();
1112 if path.as_os_str().is_empty() {
1113 return (path, false);
1116 }
1117
1118 return remap_path_prefix(&self.mapping, path);
1119
1120 #[instrument(level = "debug", skip(mapping), ret)]
1121 fn remap_path_prefix<'a>(
1122 mapping: &'a [(PathBuf, PathBuf)],
1123 path: Cow<'a, Path>,
1124 ) -> (Cow<'a, Path>, bool) {
1125 for (from, to) in mapping.iter().rev() {
1129 debug!("Trying to apply {from:?} => {to:?}");
1130
1131 if let Ok(rest) = path.strip_prefix(from) {
1132 let remapped = if rest.as_os_str().is_empty() {
1133 to.into()
1140 } else {
1141 to.join(rest).into()
1142 };
1143 debug!("Match - remapped");
1144
1145 return (remapped, true);
1146 } else {
1147 debug!("No match - prefix {from:?} does not match");
1148 }
1149 }
1150
1151 debug!("not remapped");
1152 (path, false)
1153 }
1154 }
1155
1156 fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) {
1157 match file {
1158 FileName::Real(realfile) if let RealFileName::LocalPath(local_path) = realfile => {
1159 let (mapped_path, mapped) = self.map_prefix(local_path);
1160 let realfile = if mapped {
1161 RealFileName::Remapped {
1162 local_path: Some(local_path.clone()),
1163 virtual_name: mapped_path.into_owned(),
1164 }
1165 } else {
1166 realfile.clone()
1167 };
1168 (FileName::Real(realfile), mapped)
1169 }
1170 FileName::Real(_) => unreachable!("attempted to remap an already remapped filename"),
1171 other => (other.clone(), false),
1172 }
1173 }
1174
1175 pub fn to_real_filename<'a>(&self, local_path: impl Into<Cow<'a, Path>>) -> RealFileName {
1179 let local_path = local_path.into();
1180 if let (remapped_path, true) = self.map_prefix(&*local_path) {
1181 RealFileName::Remapped {
1182 virtual_name: remapped_path.into_owned(),
1183 local_path: Some(local_path.into_owned()),
1184 }
1185 } else {
1186 RealFileName::LocalPath(local_path.into_owned())
1187 }
1188 }
1189
1190 pub fn to_embeddable_absolute_path(
1196 &self,
1197 file_path: RealFileName,
1198 working_directory: &RealFileName,
1199 ) -> RealFileName {
1200 match file_path {
1201 RealFileName::Remapped { local_path: _, virtual_name } => {
1204 RealFileName::Remapped {
1205 local_path: None,
1207 virtual_name,
1211 }
1212 }
1213
1214 RealFileName::LocalPath(unmapped_file_path) => {
1215 let (new_path, was_remapped) = self.map_prefix(unmapped_file_path);
1217 if was_remapped {
1218 return RealFileName::Remapped {
1220 local_path: None,
1221 virtual_name: new_path.into_owned(),
1222 };
1223 }
1224
1225 if new_path.is_absolute() {
1226 return RealFileName::LocalPath(new_path.into_owned());
1230 }
1231
1232 debug_assert!(new_path.is_relative());
1233 let unmapped_file_path_rel = new_path;
1234
1235 match working_directory {
1236 RealFileName::LocalPath(unmapped_working_dir_abs) => {
1237 let file_path_abs = unmapped_working_dir_abs.join(unmapped_file_path_rel);
1238
1239 let (file_path_abs, was_remapped) = self.map_prefix(file_path_abs);
1243 if was_remapped {
1244 RealFileName::Remapped {
1245 local_path: None,
1247 virtual_name: file_path_abs.into_owned(),
1248 }
1249 } else {
1250 RealFileName::LocalPath(file_path_abs.into_owned())
1253 }
1254 }
1255 RealFileName::Remapped {
1256 local_path: _,
1257 virtual_name: remapped_working_dir_abs,
1258 } => {
1259 RealFileName::Remapped {
1262 local_path: None,
1263 virtual_name: Path::new(remapped_working_dir_abs)
1264 .join(unmapped_file_path_rel),
1265 }
1266 }
1267 }
1268 }
1269 }
1270 }
1271
1272 pub fn to_local_embeddable_absolute_path(
1277 &self,
1278 file_path: RealFileName,
1279 working_directory: &RealFileName,
1280 ) -> RealFileName {
1281 let file_path = file_path.local_path_if_available();
1282 if file_path.is_absolute() {
1283 return RealFileName::LocalPath(file_path.to_path_buf());
1287 }
1288 debug_assert!(file_path.is_relative());
1289 let working_directory = working_directory.local_path_if_available();
1290 RealFileName::LocalPath(Path::new(working_directory).join(file_path))
1291 }
1292
1293 #[instrument(level = "debug", skip(self), ret)]
1305 fn reverse_map_prefix_heuristically(&self, path: &Path) -> Option<PathBuf> {
1306 let mut found = None;
1307
1308 for (from, to) in self.mapping.iter() {
1309 let has_normal_component = to.components().any(|c| match c {
1310 path::Component::Normal(s) => !s.is_empty(),
1311 _ => false,
1312 });
1313
1314 if !has_normal_component {
1315 continue;
1316 }
1317
1318 let Ok(rest) = path.strip_prefix(to) else {
1319 continue;
1320 };
1321
1322 if found.is_some() {
1323 return None;
1324 }
1325
1326 found = Some(from.join(rest));
1327 }
1328
1329 found
1330 }
1331}