More sophisticated span trimming

This commit is contained in:
Michael Goulet 2025-02-20 23:05:38 +00:00
parent 28b83ee596
commit 0a7ab1d6df
114 changed files with 932 additions and 1136 deletions

View file

@ -71,7 +71,7 @@ use rustc_macros::{Decodable, Encodable};
pub use rustc_span::ErrorGuaranteed;
pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
use rustc_span::source_map::SourceMap;
use rustc_span::{DUMMY_SP, Loc, Span};
use rustc_span::{BytePos, DUMMY_SP, Loc, Span};
pub use snippet::Style;
// Used by external projects such as `rust-gpu`.
// See https://github.com/rust-lang/rust/pull/115393.
@ -237,10 +237,9 @@ impl SubstitutionPart {
/// it with "abx" is, since the "c" character is lost.
pub fn is_destructive_replacement(&self, sm: &SourceMap) -> bool {
self.is_replacement(sm)
&& !sm.span_to_snippet(self.span).is_ok_and(|snippet| {
self.snippet.trim_start().starts_with(snippet.trim_start())
|| self.snippet.trim_end().ends_with(snippet.trim_end())
})
&& !sm
.span_to_snippet(self.span)
.is_ok_and(|snippet| as_substr(snippet.trim(), self.snippet.trim()).is_some())
}
fn replaces_meaningful_content(&self, sm: &SourceMap) -> bool {
@ -257,16 +256,40 @@ impl SubstitutionPart {
let Ok(snippet) = sm.span_to_snippet(self.span) else {
return;
};
if self.snippet.starts_with(&snippet) {
self.span = self.span.shrink_to_hi();
self.snippet = self.snippet[snippet.len()..].to_string();
} else if self.snippet.ends_with(&snippet) {
self.span = self.span.shrink_to_lo();
self.snippet = self.snippet[..self.snippet.len() - snippet.len()].to_string();
if let Some((prefix, substr, suffix)) = as_substr(&snippet, &self.snippet) {
self.span = Span::new(
self.span.lo() + BytePos(prefix as u32),
self.span.hi() - BytePos(suffix as u32),
self.span.ctxt(),
self.span.parent(),
);
self.snippet = substr.to_string();
}
}
}
/// Given an original string like `AACC`, and a suggestion like `AABBCC`, try to detect
/// the case where a substring of the suggestion is "sandwiched" in the original, like
/// `BB` is. Return the length of the prefix, the "trimmed" suggestion, and the length
/// of the suffix.
fn as_substr<'a>(original: &'a str, suggestion: &'a str) -> Option<(usize, &'a str, usize)> {
let common_prefix = original
.chars()
.zip(suggestion.chars())
.take_while(|(c1, c2)| c1 == c2)
.map(|(c, _)| c.len_utf8())
.sum();
let original = &original[common_prefix..];
let suggestion = &suggestion[common_prefix..];
if suggestion.ends_with(original) {
let common_suffix = original.len();
Some((common_prefix, &suggestion[..suggestion.len() - original.len()], common_suffix))
} else {
None
}
}
impl CodeSuggestion {
/// Returns the assembled code suggestions, whether they should be shown with an underline
/// and whether the substitution only differs in capitalization.