Rename get_source_text to get_source_range. Add new get_source_text which returns a displayable string-like type.

This commit is contained in:
Jason Newcomb 2024-08-09 09:01:41 -04:00
parent cb806113e0
commit 9afab36ca9
4 changed files with 56 additions and 29 deletions

View File

@ -22,7 +22,7 @@
/// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty
/// match arms. /// match arms.
fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
if let Some(ff) = span.get_source_text(cx) if let Some(ff) = span.get_source_range(cx)
&& let Some(text) = ff.as_str() && let Some(text) = ff.as_str()
{ {
text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*") text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")

View File

@ -687,7 +687,7 @@ fn block(&self, block: &Block<'_>) -> Option<Constant<'tcx>> {
if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
&& let expr_lo = expr_span.lo() && let expr_lo = expr_span.lo()
&& expr_lo >= span.lo && expr_lo >= span.lo
&& let Some(src) = (span.lo..expr_lo).get_source_text(&self.tcx) && let Some(src) = (span.lo..expr_lo).get_source_range(&self.tcx)
&& let Some(src) = src.as_str() && let Some(src) = src.as_str()
{ {
use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace}; use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};

View File

@ -1191,9 +1191,9 @@ fn eq_span_tokens(
pred: impl Fn(TokenKind) -> bool, pred: impl Fn(TokenKind) -> bool,
) -> bool { ) -> bool {
fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool { fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool {
if let Some(lsrc) = left.get_source_text(cx) if let Some(lsrc) = left.get_source_range(cx)
&& let Some(lsrc) = lsrc.as_str() && let Some(lsrc) = lsrc.as_str()
&& let Some(rsrc) = right.get_source_text(cx) && let Some(rsrc) = right.get_source_range(cx)
&& let Some(rsrc) = rsrc.as_str() && let Some(rsrc) = rsrc.as_str()
{ {
let pred = |t: &(_, _)| pred(t.0); let pred = |t: &(_, _)| pred(t.0);

View File

@ -16,7 +16,7 @@
}; };
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::ops::Range; use std::ops::{Deref, Range};
pub trait HasSession { pub trait HasSession {
fn sess(&self) -> &Session; fn sess(&self) -> &Session;
@ -94,10 +94,16 @@ fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
} }
pub trait SpanRangeExt: SpanRange { pub trait SpanRangeExt: SpanRange {
/// Attempts to get a handle to the source text. Returns `None` if either the span is malformed,
/// or the source text is not accessible.
fn get_source_text(self, cx: &impl HasSession) -> Option<SourceText> {
get_source_range(cx.sess().source_map(), self.into_range()).and_then(SourceText::new)
}
/// Gets the source file, and range in the file, of the given span. Returns `None` if the span /// Gets the source file, and range in the file, of the given span. Returns `None` if the span
/// extends through multiple files, or is malformed. /// extends through multiple files, or is malformed.
fn get_source_text(self, cx: &impl HasSession) -> Option<SourceFileRange> { fn get_source_range(self, cx: &impl HasSession) -> Option<SourceFileRange> {
get_source_text(cx.sess().source_map(), self.into_range()) get_source_range(cx.sess().source_map(), self.into_range())
} }
/// Calls the given function with the source text referenced and returns the value. Returns /// Calls the given function with the source text referenced and returns the value. Returns
@ -144,21 +150,49 @@ fn with_leading_whitespace(self, cx: &impl HasSession) -> Range<BytePos> {
fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> { fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> {
trim_start(cx.sess().source_map(), self.into_range()) trim_start(cx.sess().source_map(), self.into_range())
} }
/// Writes the referenced source text to the given writer. Will return `Err` if the source text
/// could not be retrieved.
fn write_source_text_to(self, cx: &impl HasSession, dst: &mut impl fmt::Write) -> fmt::Result {
write_source_text_to(cx.sess().source_map(), self.into_range(), dst)
}
/// Extracts the referenced source text as an owned string.
fn source_text_to_string(self, cx: &impl HasSession) -> Option<String> {
self.with_source_text(cx, ToOwned::to_owned)
}
} }
impl<T: SpanRange> SpanRangeExt for T {} impl<T: SpanRange> SpanRangeExt for T {}
fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> { /// Handle to a range of text in a source file.
pub struct SourceText(SourceFileRange);
impl SourceText {
/// Takes ownership of the source file handle if the source text is accessible.
pub fn new(text: SourceFileRange) -> Option<Self> {
if text.as_str().is_some() {
Some(Self(text))
} else {
None
}
}
/// Gets the source text.
pub fn as_str(&self) -> &str {
self.0.as_str().unwrap()
}
/// Converts this into an owned string.
pub fn to_owned(&self) -> String {
self.as_str().to_owned()
}
}
impl Deref for SourceText {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl AsRef<str> for SourceText {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl fmt::Display for SourceText {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}
fn get_source_range(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> {
let start = sm.lookup_byte_offset(sp.start); let start = sm.lookup_byte_offset(sp.start);
let end = sm.lookup_byte_offset(sp.end); let end = sm.lookup_byte_offset(sp.end);
if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos { if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos {
@ -169,7 +203,7 @@ fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange
} }
fn with_source_text<T>(sm: &SourceMap, sp: Range<BytePos>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> { fn with_source_text<T>(sm: &SourceMap, sp: Range<BytePos>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
if let Some(src) = get_source_text(sm, sp) if let Some(src) = get_source_range(sm, sp)
&& let Some(src) = src.as_str() && let Some(src) = src.as_str()
{ {
Some(f(src)) Some(f(src))
@ -183,7 +217,7 @@ fn with_source_text_and_range<T>(
sp: Range<BytePos>, sp: Range<BytePos>,
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T,
) -> Option<T> { ) -> Option<T> {
if let Some(src) = get_source_text(sm, sp) if let Some(src) = get_source_range(sm, sp)
&& let Some(text) = &src.sf.src && let Some(text) = &src.sf.src
{ {
Some(f(text, src.range)) Some(f(text, src.range))
@ -198,7 +232,7 @@ fn map_range(
sp: Range<BytePos>, sp: Range<BytePos>,
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>,
) -> Option<Range<BytePos>> { ) -> Option<Range<BytePos>> {
if let Some(src) = get_source_text(sm, sp.clone()) if let Some(src) = get_source_range(sm, sp.clone())
&& let Some(text) = &src.sf.src && let Some(text) = &src.sf.src
&& let Some(range) = f(text, src.range.clone()) && let Some(range) = f(text, src.range.clone())
{ {
@ -232,13 +266,6 @@ fn trim_start(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
.unwrap_or(sp) .unwrap_or(sp)
} }
fn write_source_text_to(sm: &SourceMap, sp: Range<BytePos>, dst: &mut impl fmt::Write) -> fmt::Result {
match with_source_text(sm, sp, |src| dst.write_str(src)) {
Some(x) => x,
None => Err(fmt::Error),
}
}
pub struct SourceFileRange { pub struct SourceFileRange {
pub sf: Lrc<SourceFile>, pub sf: Lrc<SourceFile>,
pub range: Range<usize>, pub range: Range<usize>,