From 3779062955fe792c3969abc1c8d4aa13525a1f17 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 31 Jul 2024 17:46:35 -0400 Subject: [PATCH] Add `HasSession` trait --- clippy_utils/src/source.rs | 129 +++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 54 deletions(-) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 496c8f5b553..96dd3c55d37 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -6,7 +6,8 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::Applicability; 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_span::source_map::{original_sp, SourceMap}; use rustc_span::{ @@ -17,6 +18,30 @@ use std::fmt; 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`. pub trait SpanRange: Sized { fn into_range(self) -> Range; @@ -71,19 +96,19 @@ fn with_ctxt(self, ctxt: SyntaxContext) -> Span { pub trait SpanRangeExt: SpanRange { /// 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. - fn get_source_text(self, cx: &impl LintContext) -> Option { + fn get_source_text(self, cx: &impl HasSession) -> Option { get_source_text(cx.sess().source_map(), self.into_range()) } /// Calls the given function with the source text referenced and returns the value. Returns /// `None` if the source text cannot be retrieved. - fn with_source_text(self, cx: &impl LintContext, f: impl for<'a> FnOnce(&'a str) -> T) -> Option { + fn with_source_text(self, cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str) -> T) -> Option { 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 /// 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) } @@ -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. fn with_source_text_and_range( self, - cx: &impl LintContext, + cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str, Range) -> T, ) -> Option { with_source_text_and_range(cx.sess().source_map(), self.into_range(), f) @@ -104,30 +129,30 @@ fn with_source_text_and_range( /// The new range must reside within the same source file. fn map_range( self, - cx: &impl LintContext, + cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str, Range) -> Option>, ) -> Option> { map_range(cx.sess().source_map(), self.into_range(), f) } /// Extends the range to include all preceding whitespace characters. - fn with_leading_whitespace(self, cx: &impl LintContext) -> Range { + fn with_leading_whitespace(self, cx: &impl HasSession) -> Range { with_leading_whitespace(cx.sess().source_map(), self.into_range()) } /// Trims the leading whitespace from the range. - fn trim_start(self, cx: &impl LintContext) -> Range { + fn trim_start(self, cx: &impl HasSession) -> 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 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) } /// Extracts the referenced source text as an owned string. - fn source_text_to_string(self, cx: &impl LintContext) -> Option { + fn source_text_to_string(self, cx: &impl HasSession) -> Option { 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`. -pub fn expr_block( - cx: &T, +pub fn expr_block( + sess: &impl HasSession, expr: &Expr<'_>, outer: SyntaxContext, default: &str, indent_relative_to: Option, app: &mut Applicability, ) -> 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 && let ExprKind::Block(block, _) = expr.kind && block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) @@ -260,13 +285,13 @@ pub fn expr_block( /// let x = (); /// // ^^^^^^^^^^ /// ``` -pub fn first_line_of_span(cx: &T, span: Span) -> Span { - first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) +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)) } -fn first_char_in_first_line(cx: &T, span: Span) -> Option { - let line_span = line_span(cx, span); - snippet_opt(cx, line_span).and_then(|snip| { +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| { snip.find(|c: char| !c.is_whitespace()) .map(|pos| line_span.lo() + BytePos::from_usize(pos)) }) @@ -281,9 +306,9 @@ fn first_char_in_first_line(cx: &T, span: Span) -> Option(cx: &T, span: Span) -> Span { +fn line_span(sess: &impl HasSession, span: Span) -> Span { 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.absolute_position(line_start); span.with_lo(line_start) @@ -297,13 +322,13 @@ fn line_span(cx: &T, span: Span) -> Span { /// let x = (); /// // ^^ -- will return 4 /// ``` -pub fn indent_of(cx: &T, span: Span) -> Option { - snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) +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())) } /// Gets a snippet of the indentation of the line of a span -pub fn snippet_indent(cx: &T, span: Span) -> Option { - snippet_opt(cx, line_span(cx, span)).map(|mut s| { +pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { + snippet_opt(sess, line_span(sess, span)).map(|mut s| { let len = s.len() - s.trim_start().len(); s.truncate(len); s @@ -315,8 +340,8 @@ pub fn snippet_indent(cx: &T, 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(cx: &T, span: Span) -> bool { - if let Some(snippet) = snippet_opt(cx, span) { +pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool { + if let Some(snippet) = snippet_opt(sess, span) { if snippet.is_empty() { return false; } @@ -407,8 +432,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, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { - snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) +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) } /// 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 default value is used and the applicability level is `MachineApplicable`, change it to /// `HasPlaceholders` -pub fn snippet_with_applicability<'a, T: LintContext>( - cx: &T, +pub fn snippet_with_applicability<'a>( + sess: &impl HasSession, span: Span, default: &'a str, applicability: &mut Applicability, ) -> 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>( @@ -435,7 +460,7 @@ fn snippet_with_applicability_sess<'a>( if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } - snippet_opt_sess(sess, span).map_or_else( + snippet_opt(sess, span).map_or_else( || { if *applicability == Applicability::MachineApplicable { *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. -pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option { - snippet_opt_sess(cx.sess(), span) -} - -fn snippet_opt_sess(sess: &Session, span: Span) -> Option { - sess.source_map().span_to_snippet(span).ok() +pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option { + sess.sess().source_map().span_to_snippet(span).ok() } /// 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 { /// } // aligned with `if` /// ``` /// Note that the first line of the snippet always has 0 indentation. -pub fn snippet_block<'a, T: LintContext>( - cx: &T, +pub fn snippet_block<'a>( + sess: &impl HasSession, span: Span, default: &'a str, indent_relative_to: Option, ) -> Cow<'a, str> { - let snip = snippet(cx, span, default); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + let snip = snippet(sess, span, default); + let indent = indent_relative_to.and_then(|s| indent_of(sess, 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<'a>( - cx: &impl LintContext, + sess: &impl HasSession, span: Span, default: &'a str, indent_relative_to: Option, applicability: &mut Applicability, ) -> Cow<'a, str> { - let snip = snippet_with_applicability(cx, span, default, applicability); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + let snip = snippet_with_applicability(sess, span, default, applicability); + let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); reindent_multiline(snip, true, indent) } pub fn snippet_block_with_context<'a>( - cx: &impl LintContext, + sess: &impl HasSession, span: Span, outer: SyntaxContext, default: &'a str, indent_relative_to: Option, app: &mut Applicability, ) -> (Cow<'a, str>, bool) { - let (snip, from_macro) = snippet_with_context(cx, span, outer, default, app); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app); + let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); (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. pub fn snippet_with_context<'a>( - cx: &impl LintContext, + sess: &impl HasSession, span: Span, outer: SyntaxContext, default: &'a str, applicability: &mut Applicability, ) -> (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>( @@ -661,15 +682,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span { /// writeln!(o, "") -> writeln!(o, "") /// ^^ ^^^^ /// ``` -pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { - let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); +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); 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( - cx: &LateContext<'_>, + sess: &impl HasSession, expr: &Expr<'_>, applicability: &mut Applicability, ascii_only: bool, @@ -684,7 +705,7 @@ pub fn str_literal_to_char_literal( } && 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 nhash = nhash as usize; // for raw string: r##"a"##