Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit dc37ff8

Browse files
committedFeb 21, 2025·
Auto merge of rust-lang#137348 - compiler-errors:span-trim, r=estebank
More sophisticated span trimming for suggestions Previously rust-lang#136958 only cared about prefixes or suffixes. Now it detects more cases where a suggestion is "sandwiched" by unchanged code on the left or the right. Would be cool if we could detect several insertions, like `ACE` going to `ABCDE`, extracting `B` and `D`, but that seems unwieldy. r? `@estebank`
2 parents 794c124 + 160905b commit dc37ff8

File tree

133 files changed

+1036
-1247
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

133 files changed

+1036
-1247
lines changed
 

‎compiler/rustc_errors/src/emitter.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2216,12 +2216,7 @@ impl HumanEmitter {
22162216
if let DisplaySuggestion::Diff | DisplaySuggestion::Underline | DisplaySuggestion::Add =
22172217
show_code_change
22182218
{
2219-
for mut part in parts {
2220-
// If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
2221-
// suggestion and snippet to look as if we just suggested to add
2222-
// `"b"`, which is typically much easier for the user to understand.
2223-
part.trim_trivial_replacements(sm);
2224-
2219+
for part in parts {
22252220
let snippet = if let Ok(snippet) = sm.span_to_snippet(part.span) {
22262221
snippet
22272222
} else {

‎compiler/rustc_errors/src/lib.rs

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ use rustc_macros::{Decodable, Encodable};
7171
pub use rustc_span::ErrorGuaranteed;
7272
pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
7373
use rustc_span::source_map::SourceMap;
74-
use rustc_span::{DUMMY_SP, Loc, Span};
74+
use rustc_span::{BytePos, DUMMY_SP, Loc, Span};
7575
pub use snippet::Style;
7676
// Used by external projects such as `rust-gpu`.
7777
// See https://github.com/rust-lang/rust/pull/115393.
@@ -237,10 +237,9 @@ impl SubstitutionPart {
237237
/// it with "abx" is, since the "c" character is lost.
238238
pub fn is_destructive_replacement(&self, sm: &SourceMap) -> bool {
239239
self.is_replacement(sm)
240-
&& !sm.span_to_snippet(self.span).is_ok_and(|snippet| {
241-
self.snippet.trim_start().starts_with(snippet.trim_start())
242-
|| self.snippet.trim_end().ends_with(snippet.trim_end())
243-
})
240+
&& !sm
241+
.span_to_snippet(self.span)
242+
.is_ok_and(|snippet| as_substr(snippet.trim(), self.snippet.trim()).is_some())
244243
}
245244

246245
fn replaces_meaningful_content(&self, sm: &SourceMap) -> bool {
@@ -257,16 +256,40 @@ impl SubstitutionPart {
257256
let Ok(snippet) = sm.span_to_snippet(self.span) else {
258257
return;
259258
};
260-
if self.snippet.starts_with(&snippet) {
261-
self.span = self.span.shrink_to_hi();
262-
self.snippet = self.snippet[snippet.len()..].to_string();
263-
} else if self.snippet.ends_with(&snippet) {
264-
self.span = self.span.shrink_to_lo();
265-
self.snippet = self.snippet[..self.snippet.len() - snippet.len()].to_string();
259+
260+
if let Some((prefix, substr, suffix)) = as_substr(&snippet, &self.snippet) {
261+
self.span = Span::new(
262+
self.span.lo() + BytePos(prefix as u32),
263+
self.span.hi() - BytePos(suffix as u32),
264+
self.span.ctxt(),
265+
self.span.parent(),
266+
);
267+
self.snippet = substr.to_string();
266268
}
267269
}
268270
}
269271

272+
/// Given an original string like `AACC`, and a suggestion like `AABBCC`, try to detect
273+
/// the case where a substring of the suggestion is "sandwiched" in the original, like
274+
/// `BB` is. Return the length of the prefix, the "trimmed" suggestion, and the length
275+
/// of the suffix.
276+
fn as_substr<'a>(original: &'a str, suggestion: &'a str) -> Option<(usize, &'a str, usize)> {
277+
let common_prefix = original
278+
.chars()
279+
.zip(suggestion.chars())
280+
.take_while(|(c1, c2)| c1 == c2)
281+
.map(|(c, _)| c.len_utf8())
282+
.sum();
283+
let original = &original[common_prefix..];
284+
let suggestion = &suggestion[common_prefix..];
285+
if suggestion.ends_with(original) {
286+
let common_suffix = original.len();
287+
Some((common_prefix, &suggestion[..suggestion.len() - original.len()], common_suffix))
288+
} else {
289+
None
290+
}
291+
}
292+
270293
impl CodeSuggestion {
271294
/// Returns the assembled code suggestions, whether they should be shown with an underline
272295
/// and whether the substitution only differs in capitalization.
@@ -380,7 +403,12 @@ impl CodeSuggestion {
380403
// or deleted code in order to point at the correct column *after* substitution.
381404
let mut acc = 0;
382405
let mut only_capitalization = false;
383-
for part in &substitution.parts {
406+
for part in &mut substitution.parts {
407+
// If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
408+
// suggestion and snippet to look as if we just suggested to add
409+
// `"b"`, which is typically much easier for the user to understand.
410+
part.trim_trivial_replacements(sm);
411+
384412
only_capitalization |= is_case_difference(sm, &part.snippet, part.span);
385413
let cur_lo = sm.lookup_char_pos(part.span.lo());
386414
if prev_hi.line == cur_lo.line {

0 commit comments

Comments
 (0)
This repository has been archived.