::with_trailing_whitespace(self.into_file_range(scx), scx)
+ }
+
+ // Extends the range to include the immediately preceding pattern. Returns `None` if the pattern
+ // does not immediately precede the range, or if the range cannot index the file's text.
+ #[inline]
+ #[must_use]
+ fn with_leading_match(self, scx: &SpanEditCx<'_>, pat: P) -> Option
+ where
+ P: Pattern,
+ for<'a> P::Searcher<'a>: ReverseSearcher<'a>,
{
- Some(f(src))
- } else {
- None
+ ::with_leading_match(self.into_file_range(scx), scx, pat)
+ }
+
+ // Extends the range to include the immediately proceeding pattern. Returns `None` if the pattern
+ // does not immediately proceed the range, or if the range cannot index the file's text.
+ #[inline]
+ #[must_use]
+ fn with_trailing_match(self, scx: &SpanEditCx<'_>, pat: impl Pattern) -> Option {
+ ::with_trailing_match(self.into_file_range(scx), scx, pat)
}
}
+impl FileRangeExt for FileRange {
+ #[inline]
+ #[cfg_attr(debug_assertions, track_caller)]
+ fn get_range_between(self, scx: &SpanEditCx<'_>, sp: impl HasSpanData) -> Option {
+ #[inline]
+ #[cfg_attr(debug_assertions, track_caller)]
+ fn f(self_: FileRange, scx: &SpanEditCx<'_>, sp: SpanData) -> Option {
+ debug_assert_eq!(scx.ctxt, sp.ctxt);
+ let file = scx.file();
+ let other = RelativeBytePos(sp.lo.0.wrapping_sub(file.start_pos.0))
+ ..RelativeBytePos(sp.hi.0.wrapping_sub(file.start_pos.0));
+ scx.dbg_check_range(None, other.clone());
+ if self_.end.0 <= other.start.0 {
+ Some(self_.end..other.start)
+ } else if self_.start.0 >= other.end.0 {
+ Some(other.end..self_.start)
+ } else {
+ None
+ }
+ }
+ f(self, scx, sp.data())
+ }
-fn with_source_text_and_range(
- sm: &SourceMap,
- sp: Range,
- f: impl for<'a> FnOnce(&'a str, Range) -> T,
-) -> Option {
- if let Some(src) = get_source_range(sm, sp)
- && let Some(text) = &src.sf.src
+ #[inline]
+ #[cfg_attr(debug_assertions, track_caller)]
+ fn extend_start_to(self, scx: &SpanEditCx<'_>, pos: PosWithCtxt) -> Option {
+ debug_assert_eq!(scx.ctxt, pos.ctxt);
+ let file = scx.file();
+ let pos = RelativeBytePos(pos.pos.0.wrapping_sub(file.start_pos.0));
+ scx.dbg_check_range(None, pos..pos);
+ (pos <= self.start).then_some(pos..self.end)
+ }
+
+ #[inline]
+ #[cfg_attr(debug_assertions, track_caller)]
+ fn extend_end_to(self, scx: &SpanEditCx<'_>, pos: PosWithCtxt) -> Option {
+ debug_assert_eq!(scx.ctxt, pos.ctxt);
+ let file = scx.file();
+ let pos = RelativeBytePos(pos.pos.0.wrapping_sub(file.start_pos.0));
+ scx.dbg_check_range(None, pos..pos);
+ (pos >= self.end).then_some(self.start..pos)
+ }
+
+ #[inline]
+ #[cfg_attr(debug_assertions, track_caller)]
+ fn shrink_start_to(self, scx: &SpanEditCx<'_>, pos: PosWithCtxt) -> Option {
+ debug_assert_eq!(scx.ctxt, pos.ctxt);
+ let file = scx.file();
+ let pos = RelativeBytePos(pos.pos.0.wrapping_sub(file.start_pos.0));
+ scx.dbg_check_range(None, pos..pos);
+ (self.start <= pos && pos <= self.end).then_some(pos..self.end)
+ }
+
+ #[inline]
+ #[cfg_attr(debug_assertions, track_caller)]
+ fn shrink_end_to(self, scx: &SpanEditCx<'_>, pos: PosWithCtxt) -> Option {
+ debug_assert_eq!(scx.ctxt, pos.ctxt);
+ let file = scx.file();
+ let pos = RelativeBytePos(pos.pos.0.wrapping_sub(file.start_pos.0));
+ scx.dbg_check_range(None, pos..pos);
+ (self.start <= pos && pos <= self.end).then_some(self.start..pos)
+ }
+
+ #[inline]
+ #[cfg_attr(debug_assertions, track_caller)]
+ #[allow(clippy::manual_map, reason = "track_caller doesn't work through `map`")]
+ fn map_range_text(self, scx: &SpanEditCx<'_>, f: impl FnOnce(&str) -> Option<&str>) -> Option {
+ let src = scx.text.as_str();
+ match src.get(self.start.to_usize()..self.end.to_usize()).and_then(f) {
+ Some(s) => {
+ let base = src.as_ptr().addr();
+ debug_assert!(
+ base <= s.as_ptr().addr() && s.as_ptr().addr() + s.len() <= base + src.len(),
+ "the string is not a valid substring",
+ );
+ let start = s.as_ptr().addr() - base;
+ Some(RelativeBytePos::from_usize(start)..RelativeBytePos::from_usize(start + s.len()))
+ },
+ None => None,
+ }
+ }
+
+ #[inline]
+ #[cfg_attr(debug_assertions, track_caller)]
+ #[allow(clippy::manual_map, reason = "track_caller doesn't work through `map`")]
+ fn map_split_range_text(
+ self,
+ scx: &SpanEditCx<'_>,
+ f: impl FnOnce(&str) -> Option<[&str; N]>,
+ ) -> Option<[FileRange; N]> {
+ let src = scx.text.as_str();
+ match src.get(self.start.to_usize()..self.end.to_usize()).and_then(f) {
+ Some(s) => {
+ let base = src.as_ptr().addr();
+ Some(map_array_inline!(s, s => {
+ debug_assert!(
+ base <= s.as_ptr().addr() && s.as_ptr().addr() + s.len() <= base + src.len(),
+ "the string is not a valid substring",
+ );
+ let start = s.as_ptr().addr() - base;
+ RelativeBytePos::from_usize(start)..RelativeBytePos::from_usize(start + s.len())
+ }))
+ },
+ None => None,
+ }
+ }
+
+ fn with_leading_whitespace(self, scx: &SpanEditCx<'_>) -> Option {
+ let src = scx.file_text();
+ let sf = scx.file();
+
+ let mut trimmed_lf = false;
+ let text_before = src.get(..self.start.to_usize())?.trim_end_matches(|c: char| {
+ trimmed_lf |= c == '\n';
+ c.is_whitespace()
+ });
+ if trimmed_lf
+ && let line_starts = sf.lines()
+ && let post_search_line = line_starts.partition_point(|&pos| pos.to_usize() <= text_before.len())
+ // `get` can fail if `line_starts` is missing the starting zero.
+ // Just start the search at the beginning in that case.
+ && let search_start = line_starts.get(post_search_line - 1).map_or(0, |&x| x.to_usize())
+ && ends_with_line_comment_or_broken(&text_before[search_start..])
+ // Is there anything after the range on the same line?
+ && !src.get(self.end.to_usize()..)?.chars().take_while(|&c| c != '\n').all(char::is_whitespace)
+ {
+ Some(self)
+ } else {
+ Some(RelativeBytePos::from_usize(text_before.len())..self.end)
+ }
+ }
+
+ fn with_trailing_whitespace(self, scx: &SpanEditCx<'_>) -> Option {
+ scx.get_text(self.end..)
+ .map(|s| self.start..RelativeBytePos::from_usize(scx.text.len() - s.trim_start().len()))
+ }
+
+ fn with_leading_match(self, scx: &SpanEditCx<'_>, pat: P) -> Option
+ where
+ P: Pattern,
+ for<'a> P::Searcher<'a>: ReverseSearcher<'a>,
{
- Some(f(text, src.range))
- } else {
- None
+ scx.get_text(..self.start)
+ .and_then(|s| s.strip_suffix(pat))
+ .map(|s| RelativeBytePos::from_usize(s.len())..self.end)
+ }
+
+ fn with_trailing_match(self, scx: &SpanEditCx<'_>, pat: impl Pattern) -> Option {
+ scx.get_text(self.end..)
+ .and_then(|s| s.strip_prefix(pat))
+ .map(|s| self.start..RelativeBytePos::from_usize(scx.text.len() - s.len()))
}
}
+impl FileRangeExt for RangeFull {}
+impl FileRangeExt for RangeTo {}
+impl FileRangeExt for RangeFrom {}
-#[expect(clippy::cast_possible_truncation)]
-fn map_range(
- sm: &SourceMap,
- sp: Range,
- f: impl for<'a> FnOnce(&'a SourceFile, &'a str, Range) -> Option>,
-) -> Option> {
- if let Some(src) = get_source_range(sm, sp.clone())
- && let Some(text) = &src.sf.src
- && let Some(range) = f(&src.sf, text, src.range.clone())
+pub trait StrExt {
+ /// Gets the substring which ranges from the start of the first match of the pattern to the end
+ /// of the second match. Returns `None` if the pattern doesn't occur twice.
+ ///
+ /// # Examples
+ /// ```rust
+ /// # #![feature(rustc_private)]
+ /// # use clippy_utils::source::StrExt;
+ /// let s = "move |arg| arg.foo()";
+ /// assert_eq!(s.find_bounded_inclusive('|'), Some("|arg|"));
+ ///
+ /// let s = "foo | bar";
+ /// assert_eq!(s.find_bounded_inclusive('|'), None)
+ /// ```
+ fn find_bounded_inclusive(&self, pat: impl Pattern) -> Option<&Self>;
+
+ /// Gets the non-overlapping prefix and suffix. Returns `None` if the string doesn't start with
+ /// the prefix or end with the suffix.
+ ///
+ /// The prefix will be taken first, with the suffix taken from the remainder of the string.
+ ///
+ /// # Examples
+ /// ```rust
+ /// # #![feature(rustc_private)]
+ /// # use clippy_utils::source::StrExt;
+ /// let s = "[a, b, c]";
+ /// assert_eq!(s.get_prefix_suffix('[', ']'), Some(["[", "]"]));
+ /// ```
+ fn get_prefix_suffix(&self, prefix: impl Pattern, suffix: P) -> Option<[&Self; 2]>
+ where
+ P: Pattern,
+ for<'a> P::Searcher<'a>: ReverseSearcher<'a>;
+
+ /// Splits a string into a prefix and everything proceeding it. Returns `None` if the string
+ /// doesn't start with the prefix.
+ ///
+ /// # Examples
+ /// ```rust
+ /// # #![feature(rustc_private)]
+ /// # use clippy_utils::source::StrExt;
+ /// let s = "fn foo()";
+ /// assert_eq!(s.split_prefix("fn"), Some(["fn", " foo()"]));
+ /// ```
+ fn split_prefix(&self, pat: impl Pattern) -> Option<[&Self; 2]>;
+
+ /// Splits a string into a suffix and everything preceding it. Returns `None` if the string
+ /// doesn't end with the suffix.
+ ///
+ /// # Examples
+ /// ```rust
+ /// # #![feature(rustc_private)]
+ /// # use clippy_utils::source::StrExt;
+ /// let s = "foo.bar()?";
+ /// assert_eq!(s.split_suffix('?'), Some(["foo.bar()", "?"]));
+ /// ```
+ fn split_suffix
(&self, pat: P) -> Option<[&Self; 2]>
+ where
+ P: Pattern,
+ for<'a> P::Searcher<'a>: ReverseSearcher<'a>;
+}
+impl StrExt for str {
+ fn find_bounded_inclusive(&self, pat: impl Pattern) -> Option<&Self> {
+ let mut iter = self.match_indices(pat);
+ if let Some((first_pos, _)) = iter.next()
+ && let Some((second_pos, second)) = iter.next()
+ {
+ Some(&self[first_pos..second_pos + second.len()])
+ } else {
+ None
+ }
+ }
+
+ fn get_prefix_suffix
(&self, prefix: impl Pattern, suffix: P) -> Option<[&Self; 2]>
+ where
+ P: Pattern,
+ for<'a> P::Searcher<'a>: ReverseSearcher<'a>,
{
- debug_assert!(
- range.start <= text.len() && range.end <= text.len(),
- "Range `{range:?}` is outside the source file (file `{}`, length `{}`)",
- src.sf.name.prefer_local_unconditionally(),
- text.len(),
- );
- debug_assert!(range.start <= range.end, "Range `{range:?}` has overlapping bounds");
- let dstart = (range.start as u32).wrapping_sub(src.range.start as u32);
- let dend = (range.end as u32).wrapping_sub(src.range.start as u32);
- Some(BytePos(sp.start.0.wrapping_add(dstart))..BytePos(sp.start.0.wrapping_add(dend)))
- } else {
- None
+ if let Some([pre, s]) = self.split_prefix(prefix)
+ && let Some([_, suf]) = s.split_suffix(suffix)
+ {
+ Some([pre, suf])
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn split_prefix(&self, pat: impl Pattern) -> Option<[&Self; 2]> {
+ self.strip_prefix(pat)
+ .map(|rest| [&self[..self.len() - rest.len()], rest])
+ }
+
+ #[inline]
+ fn split_suffix
(&self, pat: P) -> Option<[&Self; 2]>
+ where
+ P: Pattern,
+ for<'a> P::Searcher<'a>: ReverseSearcher<'a>,
+ {
+ self.strip_suffix(pat).map(|rest| [rest, &self[rest.len()..]])
}
}
+/// Checks if the last token of the string is either a line comment or an incomplete token.
fn ends_with_line_comment_or_broken(text: &str) -> bool {
let Some(last) = tokenize(text, FrontmatterAllowed::No).last() else {
return false;
@@ -303,65 +1528,16 @@ fn ends_with_line_comment_or_broken(text: &str) -> bool {
}
}
-fn with_leading_whitespace_inner(lines: &[RelativeBytePos], src: &str, range: Range) -> Option {
- debug_assert!(lines.is_empty() || lines[0].to_u32() == 0);
-
- let start = src.get(..range.start)?.trim_end();
- let next_line = lines.partition_point(|&pos| pos.to_usize() <= start.len());
- if let Some(line_end) = lines.get(next_line)
- && line_end.to_usize() <= range.start
- && let prev_start = lines.get(next_line - 1).map_or(0, |&x| x.to_usize())
- && ends_with_line_comment_or_broken(&start[prev_start..])
- && let next_line = lines.partition_point(|&pos| pos.to_usize() < range.end)
- && let next_start = lines.get(next_line).map_or(src.len(), |&x| x.to_usize())
- && tokenize(src.get(range.end..next_start)?, FrontmatterAllowed::No)
- .any(|t| !matches!(t.kind, TokenKind::Whitespace))
- {
- Some(range.start)
- } else {
- Some(start.len())
- }
-}
-
-fn with_leading_whitespace(sm: &SourceMap, sp: Range) -> Range {
- map_range(sm, sp.clone(), |sf, src, range| {
- Some(with_leading_whitespace_inner(sf.lines(), src, range.clone())?..range.end)
- })
- .unwrap_or(sp)
-}
-
-fn trim_start(sm: &SourceMap, sp: Range) -> Range {
- map_range(sm, sp.clone(), |_, src, range| {
- let src = src.get(range.clone())?;
- Some(range.start + (src.len() - src.trim_start().len())..range.end)
- })
- .unwrap_or(sp)
-}
-
-pub struct SourceFileRange {
- pub sf: Arc,
- pub range: Range,
-}
-impl SourceFileRange {
- /// Attempts to get the text from the source file. This can fail if the source text isn't
- /// loaded.
- pub fn as_str(&self) -> Option<&str> {
- (self.sf.src.as_ref().map(|src| src.as_str()))
- .or_else(|| self.sf.external_src.get()?.get_source())
- .and_then(|x| x.get(self.range.clone()))
- }
-}
-
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block` with no label.
-pub fn expr_block(
- sess: &impl HasSession,
+pub fn expr_block<'sm>(
+ sm: impl HasSourceMap<'sm>,
expr: &Expr<'_>,
outer: SyntaxContext,
default: &str,
indent_relative_to: Option,
app: &mut Applicability,
) -> String {
- let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app);
+ let (code, from_macro) = snippet_block_with_context(sm, expr.span, outer, default, indent_relative_to, app);
if !from_macro
&& let ExprKind::Block(block, None) = expr.kind
&& block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
@@ -386,13 +1562,13 @@ pub fn expr_block(
/// let x = ();
/// // ^^^^^^^^^^
/// ```
-pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span {
- first_char_in_first_line(sess, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
+pub fn first_line_of_span<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span {
+ first_char_in_first_line(sm, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
}
-fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option {
- let line_span = line_span(sess, span);
- snippet_opt(sess, line_span).and_then(|snip| {
+fn first_char_in_first_line<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option {
+ let line_span = line_span(sm, span);
+ snippet_opt(sm, line_span).and_then(|snip| {
snip.find(|c: char| !c.is_whitespace())
.map(|pos| line_span.lo() + BytePos::from_usize(pos))
})
@@ -407,9 +1583,9 @@ fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option Span {
+fn line_span<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span {
let span = original_sp(span, DUMMY_SP);
- let SourceFileAndLine { sf, line } = sess.sess().source_map().lookup_line(span.lo()).unwrap();
+ let SourceFileAndLine { sf, line } = sm.source_map().lookup_line(span.lo()).unwrap();
let line_start = sf.lines()[line];
let line_start = sf.absolute_position(line_start);
span.with_lo(line_start)
@@ -423,13 +1599,13 @@ fn line_span(sess: &impl HasSession, span: Span) -> Span {
/// let x = ();
/// // ^^ -- will return 4
/// ```
-pub fn indent_of(sess: &impl HasSession, span: Span) -> Option {
- snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
+pub fn indent_of<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option {
+ snippet_opt(sm, line_span(sm, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
}
/// Gets a snippet of the indentation of the line of a span
-pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option {
- snippet_opt(sess, line_span(sess, span)).map(|mut s| {
+pub fn snippet_indent<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option {
+ snippet_opt(sm, line_span(sm, span)).map(|mut s| {
let len = s.len() - s.trim_start().len();
s.truncate(len);
s
@@ -441,8 +1617,8 @@ pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option {
// sources that the user has no control over.
// For some reason these attributes don't have any expansion info on them, so
// we have to check it this way until there is a better way.
-pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool {
- if let Some(snippet) = snippet_opt(sess, span)
+pub fn is_present_in_source<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool {
+ if let Some(snippet) = snippet_opt(sm, span)
&& snippet.is_empty()
{
return false;
@@ -532,8 +1708,8 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option,
/// snippet(cx, span1, "..") // -> "value"
/// snippet(cx, span2, "..") // -> "Vec::new()"
/// ```
-pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> {
- snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from)
+pub fn snippet<'a, 'sm>(sm: impl HasSourceMap<'sm>, span: Span, default: &'a str) -> Cow<'a, str> {
+ snippet_opt(sm, span).map_or_else(|| Cow::Borrowed(default), From::from)
}
/// Same as [`snippet`], but it adapts the applicability level by following rules:
@@ -542,17 +1718,17 @@ pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<
/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
/// `HasPlaceholders`
-pub fn snippet_with_applicability<'a>(
- sess: &impl HasSession,
+pub fn snippet_with_applicability<'a, 'sm>(
+ sm: impl HasSourceMap<'sm>,
span: Span,
default: &'a str,
applicability: &mut Applicability,
) -> Cow<'a, str> {
- snippet_with_applicability_sess(sess.sess(), span, default, applicability)
+ snippet_with_applicability_sm(sm.source_map(), span, default, applicability)
}
-fn snippet_with_applicability_sess<'a>(
- sess: &Session,
+fn snippet_with_applicability_sm<'a>(
+ sm: &SourceMap,
span: Span,
default: &'a str,
applicability: &mut Applicability,
@@ -560,7 +1736,7 @@ fn snippet_with_applicability_sess<'a>(
if *applicability != Applicability::Unspecified && span.from_expansion() {
*applicability = Applicability::MaybeIncorrect;
}
- snippet_opt(sess, span).map_or_else(
+ snippet_opt(sm, span).map_or_else(
|| {
if *applicability == Applicability::MachineApplicable {
*applicability = Applicability::HasPlaceholders;
@@ -572,8 +1748,8 @@ fn snippet_with_applicability_sess<'a>(
}
/// Converts a span to a code snippet. Returns `None` if not available.
-pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option {
- sess.sess().source_map().span_to_snippet(span).ok()
+pub fn snippet_opt<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option {
+ sm.source_map().span_to_snippet(span).ok()
}
/// Converts a span (from a block) to a code snippet if available, otherwise use default.
@@ -610,36 +1786,41 @@ pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option {
/// } // aligned with `if`
/// ```
/// Note that the first line of the snippet always has 0 indentation.
-pub fn snippet_block(sess: &impl HasSession, span: Span, default: &str, indent_relative_to: Option) -> String {
- let snip = snippet(sess, span, default);
- let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
+pub fn snippet_block<'sm>(
+ sm: impl HasSourceMap<'sm>,
+ span: Span,
+ default: &str,
+ indent_relative_to: Option,
+) -> String {
+ let snip = snippet(sm, span, default);
+ let indent = indent_relative_to.and_then(|s| indent_of(sm, s));
reindent_multiline(&snip, true, indent)
}
/// Same as `snippet_block`, but adapts the applicability level by the rules of
/// `snippet_with_applicability`.
-pub fn snippet_block_with_applicability(
- sess: &impl HasSession,
+pub fn snippet_block_with_applicability<'sm>(
+ sm: impl HasSourceMap<'sm>,
span: Span,
default: &str,
indent_relative_to: Option,
applicability: &mut Applicability,
) -> String {
- let snip = snippet_with_applicability(sess, span, default, applicability);
- let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
+ let snip = snippet_with_applicability(sm, span, default, applicability);
+ let indent = indent_relative_to.and_then(|s| indent_of(sm, s));
reindent_multiline(&snip, true, indent)
}
-pub fn snippet_block_with_context(
- sess: &impl HasSession,
+pub fn snippet_block_with_context<'sm>(
+ sm: impl HasSourceMap<'sm>,
span: Span,
outer: SyntaxContext,
default: &str,
indent_relative_to: Option,
app: &mut Applicability,
) -> (String, bool) {
- let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app);
- let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
+ let (snip, from_macro) = snippet_with_context(sm, span, outer, default, app);
+ let indent = indent_relative_to.and_then(|s| indent_of(sm, s));
(reindent_multiline(&snip, true, indent), from_macro)
}
@@ -653,18 +1834,18 @@ pub fn snippet_block_with_context(
/// correctly get a snippet of `vec![]`.
///
/// This will also return whether or not the snippet is a macro call.
-pub fn snippet_with_context<'a>(
- sess: &impl HasSession,
+pub fn snippet_with_context<'a, 'sm>(
+ sm: impl HasSourceMap<'sm>,
span: Span,
outer: SyntaxContext,
default: &'a str,
applicability: &mut Applicability,
) -> (Cow<'a, str>, bool) {
- snippet_with_context_sess(sess.sess(), span, outer, default, applicability)
+ snippet_with_context_sm(sm.source_map(), span, outer, default, applicability)
}
-fn snippet_with_context_sess<'a>(
- sess: &Session,
+fn snippet_with_context_sm<'a>(
+ sm: &SourceMap,
span: Span,
outer: SyntaxContext,
default: &'a str,
@@ -672,10 +1853,7 @@ fn snippet_with_context_sess<'a>(
) -> (Cow<'a, str>, bool) {
// If it is just range desugaring, use the desugaring span since it may include parenthesis.
if span.desugaring_kind() == Some(DesugaringKind::RangeExpr) && span.parent_callsite().unwrap().ctxt() == outer {
- return (
- snippet_with_applicability_sess(sess, span, default, applicability),
- false,
- );
+ return (snippet_with_applicability_sm(sm, span, default, applicability), false);
}
let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else(
@@ -691,7 +1869,7 @@ fn snippet_with_context_sess<'a>(
);
(
- snippet_with_applicability_sess(sess, span, default, applicability),
+ snippet_with_applicability_sm(sm, span, default, applicability),
is_macro_call,
)
}
@@ -754,15 +1932,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span {
/// writeln!(o, "") -> writeln!(o, "")
/// ^^ ^^^^
/// ```
-pub fn expand_past_previous_comma(sess: &impl HasSession, span: Span) -> Span {
- let extended = sess.sess().source_map().span_extend_to_prev_char(span, ',', true);
+pub fn expand_past_previous_comma<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span {
+ let extended = sm.source_map().span_extend_to_prev_char(span, ',', true);
extended.with_lo(extended.lo() - BytePos(1))
}
/// Converts `expr` to a `char` literal if it's a `str` literal containing a single
/// character (or a single byte with `ascii_only`)
-pub fn str_literal_to_char_literal(
- sess: &impl HasSession,
+pub fn str_literal_to_char_literal<'sm>(
+ sm: impl HasSourceMap<'sm>,
expr: &Expr<'_>,
applicability: &mut Applicability,
ascii_only: bool,
@@ -777,7 +1955,7 @@ pub fn str_literal_to_char_literal(
}
&& len == 1
{
- let snip = snippet_with_applicability(sess, expr.span, string, applicability);
+ let snip = snippet_with_applicability(sm, expr.span, string, applicability);
let ch = if let StrStyle::Raw(nhash) = style {
let nhash = nhash as usize;
// for raw string: r##"a"##