//! This module contains utilities that work with the `SourceMap` from `libsyntax`/`syntex_syntax`. //! This includes extension traits and methods for looking up spans and line ranges for AST nodes. use syntax::source_map::{BytePos, SourceMap, Span}; use crate::comment::FindUncommented; use crate::config::file_lines::LineRange; use crate::visitor::SnippetProvider; pub(crate) trait SpanUtils { fn span_after(&self, original: Span, needle: &str) -> BytePos; fn span_after_last(&self, original: Span, needle: &str) -> BytePos; fn span_before(&self, original: Span, needle: &str) -> BytePos; fn span_before_last(&self, original: Span, needle: &str) -> BytePos; fn opt_span_after(&self, original: Span, needle: &str) -> Option; fn opt_span_before(&self, original: Span, needle: &str) -> Option; } pub(crate) trait LineRangeUtils { /// Returns the `LineRange` that corresponds to `span` in `self`. /// /// # Panics /// /// Panics if `span` crosses a file boundary, which shouldn't happen. fn lookup_line_range(&self, span: Span) -> LineRange; } impl<'a> SpanUtils for SnippetProvider<'a> { fn span_after(&self, original: Span, needle: &str) -> BytePos { self.opt_span_after(original, needle).unwrap_or_else(|| { panic!( "bad span: `{}`: `{}`", needle, self.span_to_snippet(original).unwrap() ) }) } fn span_after_last(&self, original: Span, needle: &str) -> BytePos { let snippet = self.span_to_snippet(original).unwrap(); let mut offset = 0; while let Some(additional_offset) = snippet[offset..].find_uncommented(needle) { offset += additional_offset + needle.len(); } original.lo() + BytePos(offset as u32) } fn span_before(&self, original: Span, needle: &str) -> BytePos { self.opt_span_before(original, needle).unwrap_or_else(|| { panic!( "bad span: `{}`: `{}`", needle, self.span_to_snippet(original).unwrap() ) }) } fn span_before_last(&self, original: Span, needle: &str) -> BytePos { let snippet = self.span_to_snippet(original).unwrap(); let mut offset = 0; while let Some(additional_offset) = snippet[offset..].find_uncommented(needle) { offset += additional_offset + needle.len(); } original.lo() + BytePos(offset as u32 - 1) } fn opt_span_after(&self, original: Span, needle: &str) -> Option { self.opt_span_before(original, needle) .map(|bytepos| bytepos + BytePos(needle.len() as u32)) } fn opt_span_before(&self, original: Span, needle: &str) -> Option { let snippet = self.span_to_snippet(original)?; let offset = snippet.find_uncommented(needle)?; Some(original.lo() + BytePos(offset as u32)) } } impl LineRangeUtils for SourceMap { fn lookup_line_range(&self, span: Span) -> LineRange { let snippet = self.span_to_snippet(span).unwrap_or_default(); let lo = self.lookup_line(span.lo()).unwrap(); let hi = self.lookup_line(span.hi()).unwrap(); debug_assert_eq!( lo.sf.name, hi.sf.name, "span crossed file boundary: lo: {:?}, hi: {:?}", lo, hi ); // in case the span starts with a newline, the line range is off by 1 without the // adjustment below let offset = 1 + if snippet.starts_with('\n') { 1 } else { 0 }; // Line numbers start at 1 LineRange { file: lo.sf.clone(), lo: lo.line + offset, hi: hi.line + offset, } } }