Add HasSession trait

This commit is contained in:
Jason Newcomb 2024-07-31 17:46:35 -04:00
parent 5ccf5432ab
commit 3779062955

View File

@ -6,7 +6,8 @@
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{EarlyContext, LateContext};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session; use rustc_session::Session;
use rustc_span::source_map::{original_sp, SourceMap}; use rustc_span::source_map::{original_sp, SourceMap};
use rustc_span::{ use rustc_span::{
@ -17,6 +18,30 @@
use std::fmt; use std::fmt;
use std::ops::Range; use std::ops::Range;
pub trait HasSession {
fn sess(&self) -> &Session;
}
impl HasSession for Session {
fn sess(&self) -> &Session {
self
}
}
impl HasSession for TyCtxt<'_> {
fn sess(&self) -> &Session {
self.sess
}
}
impl HasSession for EarlyContext<'_> {
fn sess(&self) -> &Session {
::rustc_lint::LintContext::sess(self)
}
}
impl HasSession for LateContext<'_> {
fn sess(&self) -> &Session {
self.tcx.sess()
}
}
/// Conversion of a value into the range portion of a `Span`. /// Conversion of a value into the range portion of a `Span`.
pub trait SpanRange: Sized { pub trait SpanRange: Sized {
fn into_range(self) -> Range<BytePos>; fn into_range(self) -> Range<BytePos>;
@ -71,19 +96,19 @@ fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
pub trait SpanRangeExt: SpanRange { pub trait SpanRangeExt: SpanRange {
/// 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 LintContext) -> Option<SourceFileRange> { fn get_source_text(self, cx: &impl HasSession) -> Option<SourceFileRange> {
get_source_text(cx.sess().source_map(), self.into_range()) get_source_text(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
/// `None` if the source text cannot be retrieved. /// `None` if the source text cannot be retrieved.
fn with_source_text<T>(self, cx: &impl LintContext, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> { fn with_source_text<T>(self, cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
with_source_text(cx.sess().source_map(), self.into_range(), f) with_source_text(cx.sess().source_map(), self.into_range(), f)
} }
/// Checks if the referenced source text satisfies the given predicate. Returns `false` if the /// Checks if the referenced source text satisfies the given predicate. Returns `false` if the
/// source text cannot be retrieved. /// source text cannot be retrieved.
fn check_source_text(self, cx: &impl LintContext, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { fn check_source_text(self, cx: &impl HasSession, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool {
self.with_source_text(cx, pred).unwrap_or(false) self.with_source_text(cx, pred).unwrap_or(false)
} }
@ -91,7 +116,7 @@ fn check_source_text(self, cx: &impl LintContext, pred: impl for<'a> FnOnce(&'a
/// and returns the value. Returns `None` if the source text cannot be retrieved. /// and returns the value. Returns `None` if the source text cannot be retrieved.
fn with_source_text_and_range<T>( fn with_source_text_and_range<T>(
self, self,
cx: &impl LintContext, cx: &impl HasSession,
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T,
) -> Option<T> { ) -> Option<T> {
with_source_text_and_range(cx.sess().source_map(), self.into_range(), f) with_source_text_and_range(cx.sess().source_map(), self.into_range(), f)
@ -104,30 +129,30 @@ fn with_source_text_and_range<T>(
/// The new range must reside within the same source file. /// The new range must reside within the same source file.
fn map_range( fn map_range(
self, self,
cx: &impl LintContext, cx: &impl HasSession,
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>> {
map_range(cx.sess().source_map(), self.into_range(), f) map_range(cx.sess().source_map(), self.into_range(), f)
} }
/// Extends the range to include all preceding whitespace characters. /// Extends the range to include all preceding whitespace characters.
fn with_leading_whitespace(self, cx: &impl LintContext) -> Range<BytePos> { fn with_leading_whitespace(self, cx: &impl HasSession) -> Range<BytePos> {
with_leading_whitespace(cx.sess().source_map(), self.into_range()) with_leading_whitespace(cx.sess().source_map(), self.into_range())
} }
/// Trims the leading whitespace from the range. /// Trims the leading whitespace from the range.
fn trim_start(self, cx: &impl LintContext) -> 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 /// Writes the referenced source text to the given writer. Will return `Err` if the source text
/// could not be retrieved. /// could not be retrieved.
fn write_source_text_to(self, cx: &impl LintContext, dst: &mut impl fmt::Write) -> fmt::Result { 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) write_source_text_to(cx.sess().source_map(), self.into_range(), dst)
} }
/// Extracts the referenced source text as an owned string. /// Extracts the referenced source text as an owned string.
fn source_text_to_string(self, cx: &impl LintContext) -> Option<String> { fn source_text_to_string(self, cx: &impl HasSession) -> Option<String> {
self.with_source_text(cx, ToOwned::to_owned) self.with_source_text(cx, ToOwned::to_owned)
} }
} }
@ -227,15 +252,15 @@ pub fn as_str(&self) -> Option<&str> {
} }
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
pub fn expr_block<T: LintContext>( pub fn expr_block(
cx: &T, sess: &impl HasSession,
expr: &Expr<'_>, expr: &Expr<'_>,
outer: SyntaxContext, outer: SyntaxContext,
default: &str, default: &str,
indent_relative_to: Option<Span>, indent_relative_to: Option<Span>,
app: &mut Applicability, app: &mut Applicability,
) -> String { ) -> String {
let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app); let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app);
if !from_macro if !from_macro
&& let ExprKind::Block(block, _) = expr.kind && let ExprKind::Block(block, _) = expr.kind
&& block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) && block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
@ -260,13 +285,13 @@ pub fn expr_block<T: LintContext>(
/// let x = (); /// let x = ();
/// // ^^^^^^^^^^ /// // ^^^^^^^^^^
/// ``` /// ```
pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span { pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span {
first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) first_char_in_first_line(sess, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
} }
fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> { fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option<BytePos> {
let line_span = line_span(cx, span); let line_span = line_span(sess, span);
snippet_opt(cx, line_span).and_then(|snip| { snippet_opt(sess, line_span).and_then(|snip| {
snip.find(|c: char| !c.is_whitespace()) snip.find(|c: char| !c.is_whitespace())
.map(|pos| line_span.lo() + BytePos::from_usize(pos)) .map(|pos| line_span.lo() + BytePos::from_usize(pos))
}) })
@ -281,9 +306,9 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo
/// let x = (); /// let x = ();
/// // ^^^^^^^^^^^^^^ /// // ^^^^^^^^^^^^^^
/// ``` /// ```
fn line_span<T: LintContext>(cx: &T, span: Span) -> Span { fn line_span(sess: &impl HasSession, span: Span) -> Span {
let span = original_sp(span, DUMMY_SP); let span = original_sp(span, DUMMY_SP);
let SourceFileAndLine { sf, line } = cx.sess().source_map().lookup_line(span.lo()).unwrap(); let SourceFileAndLine { sf, line } = sess.sess().source_map().lookup_line(span.lo()).unwrap();
let line_start = sf.lines()[line]; let line_start = sf.lines()[line];
let line_start = sf.absolute_position(line_start); let line_start = sf.absolute_position(line_start);
span.with_lo(line_start) span.with_lo(line_start)
@ -297,13 +322,13 @@ fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
/// let x = (); /// let x = ();
/// // ^^ -- will return 4 /// // ^^ -- will return 4
/// ``` /// ```
pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> { pub fn indent_of(sess: &impl HasSession, span: Span) -> Option<usize> {
snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
} }
/// Gets a snippet of the indentation of the line of a span /// Gets a snippet of the indentation of the line of a span
pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> { pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option<String> {
snippet_opt(cx, line_span(cx, span)).map(|mut s| { snippet_opt(sess, line_span(sess, span)).map(|mut s| {
let len = s.len() - s.trim_start().len(); let len = s.len() - s.trim_start().len();
s.truncate(len); s.truncate(len);
s s
@ -315,8 +340,8 @@ pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
// sources that the user has no control over. // sources that the user has no control over.
// For some reason these attributes don't have any expansion info on them, so // 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. // we have to check it this way until there is a better way.
pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool { pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool {
if let Some(snippet) = snippet_opt(cx, span) { if let Some(snippet) = snippet_opt(sess, span) {
if snippet.is_empty() { if snippet.is_empty() {
return false; return false;
} }
@ -407,8 +432,8 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>,
/// snippet(cx, span1, "..") // -> "value" /// snippet(cx, span1, "..") // -> "value"
/// snippet(cx, span2, "..") // -> "Vec::new()" /// snippet(cx, span2, "..") // -> "Vec::new()"
/// ``` /// ```
pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> {
snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from)
} }
/// Same as [`snippet`], but it adapts the applicability level by following rules: /// Same as [`snippet`], but it adapts the applicability level by following rules:
@ -417,13 +442,13 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<
/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - 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 /// - If the default value is used and the applicability level is `MachineApplicable`, change it to
/// `HasPlaceholders` /// `HasPlaceholders`
pub fn snippet_with_applicability<'a, T: LintContext>( pub fn snippet_with_applicability<'a>(
cx: &T, sess: &impl HasSession,
span: Span, span: Span,
default: &'a str, default: &'a str,
applicability: &mut Applicability, applicability: &mut Applicability,
) -> Cow<'a, str> { ) -> Cow<'a, str> {
snippet_with_applicability_sess(cx.sess(), span, default, applicability) snippet_with_applicability_sess(sess.sess(), span, default, applicability)
} }
fn snippet_with_applicability_sess<'a>( fn snippet_with_applicability_sess<'a>(
@ -435,7 +460,7 @@ fn snippet_with_applicability_sess<'a>(
if *applicability != Applicability::Unspecified && span.from_expansion() { if *applicability != Applicability::Unspecified && span.from_expansion() {
*applicability = Applicability::MaybeIncorrect; *applicability = Applicability::MaybeIncorrect;
} }
snippet_opt_sess(sess, span).map_or_else( snippet_opt(sess, span).map_or_else(
|| { || {
if *applicability == Applicability::MachineApplicable { if *applicability == Applicability::MachineApplicable {
*applicability = Applicability::HasPlaceholders; *applicability = Applicability::HasPlaceholders;
@ -447,12 +472,8 @@ fn snippet_with_applicability_sess<'a>(
} }
/// Converts a span to a code snippet. Returns `None` if not available. /// Converts a span to a code snippet. Returns `None` if not available.
pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> { pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option<String> {
snippet_opt_sess(cx.sess(), span) sess.sess().source_map().span_to_snippet(span).ok()
}
fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
sess.source_map().span_to_snippet(span).ok()
} }
/// Converts a span (from a block) to a code snippet if available, otherwise use default. /// Converts a span (from a block) to a code snippet if available, otherwise use default.
@ -489,41 +510,41 @@ fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
/// } // aligned with `if` /// } // aligned with `if`
/// ``` /// ```
/// Note that the first line of the snippet always has 0 indentation. /// Note that the first line of the snippet always has 0 indentation.
pub fn snippet_block<'a, T: LintContext>( pub fn snippet_block<'a>(
cx: &T, sess: &impl HasSession,
span: Span, span: Span,
default: &'a str, default: &'a str,
indent_relative_to: Option<Span>, indent_relative_to: Option<Span>,
) -> Cow<'a, str> { ) -> Cow<'a, str> {
let snip = snippet(cx, span, default); let snip = snippet(sess, span, default);
let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
reindent_multiline(snip, true, indent) reindent_multiline(snip, true, indent)
} }
/// Same as `snippet_block`, but adapts the applicability level by the rules of /// Same as `snippet_block`, but adapts the applicability level by the rules of
/// `snippet_with_applicability`. /// `snippet_with_applicability`.
pub fn snippet_block_with_applicability<'a>( pub fn snippet_block_with_applicability<'a>(
cx: &impl LintContext, sess: &impl HasSession,
span: Span, span: Span,
default: &'a str, default: &'a str,
indent_relative_to: Option<Span>, indent_relative_to: Option<Span>,
applicability: &mut Applicability, applicability: &mut Applicability,
) -> Cow<'a, str> { ) -> Cow<'a, str> {
let snip = snippet_with_applicability(cx, span, default, applicability); let snip = snippet_with_applicability(sess, span, default, applicability);
let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
reindent_multiline(snip, true, indent) reindent_multiline(snip, true, indent)
} }
pub fn snippet_block_with_context<'a>( pub fn snippet_block_with_context<'a>(
cx: &impl LintContext, sess: &impl HasSession,
span: Span, span: Span,
outer: SyntaxContext, outer: SyntaxContext,
default: &'a str, default: &'a str,
indent_relative_to: Option<Span>, indent_relative_to: Option<Span>,
app: &mut Applicability, app: &mut Applicability,
) -> (Cow<'a, str>, bool) { ) -> (Cow<'a, str>, bool) {
let (snip, from_macro) = snippet_with_context(cx, span, outer, default, app); let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app);
let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
(reindent_multiline(snip, true, indent), from_macro) (reindent_multiline(snip, true, indent), from_macro)
} }
@ -537,13 +558,13 @@ pub fn snippet_block_with_context<'a>(
/// ///
/// This will also return whether or not the snippet is a macro call. /// This will also return whether or not the snippet is a macro call.
pub fn snippet_with_context<'a>( pub fn snippet_with_context<'a>(
cx: &impl LintContext, sess: &impl HasSession,
span: Span, span: Span,
outer: SyntaxContext, outer: SyntaxContext,
default: &'a str, default: &'a str,
applicability: &mut Applicability, applicability: &mut Applicability,
) -> (Cow<'a, str>, bool) { ) -> (Cow<'a, str>, bool) {
snippet_with_context_sess(cx.sess(), span, outer, default, applicability) snippet_with_context_sess(sess.sess(), span, outer, default, applicability)
} }
fn snippet_with_context_sess<'a>( fn snippet_with_context_sess<'a>(
@ -661,15 +682,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span {
/// writeln!(o, "") -> writeln!(o, "") /// writeln!(o, "") -> writeln!(o, "")
/// ^^ ^^^^ /// ^^ ^^^^
/// ``` /// ```
pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { pub fn expand_past_previous_comma(sess: &impl HasSession, span: Span) -> Span {
let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); let extended = sess.sess().source_map().span_extend_to_prev_char(span, ',', true);
extended.with_lo(extended.lo() - BytePos(1)) extended.with_lo(extended.lo() - BytePos(1))
} }
/// Converts `expr` to a `char` literal if it's a `str` literal containing a single /// Converts `expr` to a `char` literal if it's a `str` literal containing a single
/// character (or a single byte with `ascii_only`) /// character (or a single byte with `ascii_only`)
pub fn str_literal_to_char_literal( pub fn str_literal_to_char_literal(
cx: &LateContext<'_>, sess: &impl HasSession,
expr: &Expr<'_>, expr: &Expr<'_>,
applicability: &mut Applicability, applicability: &mut Applicability,
ascii_only: bool, ascii_only: bool,
@ -684,7 +705,7 @@ pub fn str_literal_to_char_literal(
} }
&& len == 1 && len == 1
{ {
let snip = snippet_with_applicability(cx, expr.span, string, applicability); let snip = snippet_with_applicability(sess, expr.span, string, applicability);
let ch = if let StrStyle::Raw(nhash) = style { let ch = if let StrStyle::Raw(nhash) = style {
let nhash = nhash as usize; let nhash = nhash as usize;
// for raw string: r##"a"## // for raw string: r##"a"##