Skip to content

Commit 4117933

Browse files
committed
fix: Use untrimmed line numbers for trimmed suggestions
1 parent f4a31d7 commit 4117933

File tree

3 files changed

+54
-35
lines changed

3 files changed

+54
-35
lines changed

compiler/rustc_errors/src/emitter.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2154,11 +2154,11 @@ impl HumanEmitter {
21542154

21552155
assert!(!file_lines.lines.is_empty() || parts[0].span.is_dummy());
21562156

2157-
let line_start = sm.lookup_char_pos(parts[0].span.lo()).line;
2157+
let line_start = sm.lookup_char_pos(parts[0].original_span.lo()).line;
21582158
let mut lines = complete.lines();
21592159
if lines.clone().next().is_none() {
21602160
// Account for a suggestion to completely remove a line(s) with whitespace (#94192).
2161-
let line_end = sm.lookup_char_pos(parts[0].span.hi()).line;
2161+
let line_end = sm.lookup_char_pos(parts[0].original_span.hi()).line;
21622162
for line in line_start..=line_end {
21632163
self.draw_line_num(
21642164
&mut buffer,

compiler/rustc_errors/src/lib.rs

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,13 @@ pub struct SubstitutionPart {
224224
pub snippet: String,
225225
}
226226

227+
#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
228+
pub struct TrimmedSubstitutionPart {
229+
pub original_span: Span,
230+
pub span: Span,
231+
pub snippet: String,
232+
}
233+
227234
/// Used to translate between `Span`s and byte positions within a single output line in highlighted
228235
/// code of structured suggestions.
229236
#[derive(Debug, Clone, Copy)]
@@ -233,6 +240,35 @@ pub(crate) struct SubstitutionHighlight {
233240
}
234241

235242
impl SubstitutionPart {
243+
/// Try to turn a replacement into an addition when the span that is being
244+
/// overwritten matches either the prefix or suffix of the replacement.
245+
fn trim_trivial_replacements(self, sm: &SourceMap) -> TrimmedSubstitutionPart {
246+
let mut trimmed_part = TrimmedSubstitutionPart {
247+
original_span: self.span,
248+
span: self.span,
249+
snippet: self.snippet,
250+
};
251+
if trimmed_part.snippet.is_empty() {
252+
return trimmed_part;
253+
}
254+
let Ok(snippet) = sm.span_to_snippet(trimmed_part.span) else {
255+
return trimmed_part;
256+
};
257+
258+
if let Some((prefix, substr, suffix)) = as_substr(&snippet, &trimmed_part.snippet) {
259+
trimmed_part.span = Span::new(
260+
trimmed_part.span.lo() + BytePos(prefix as u32),
261+
trimmed_part.span.hi() - BytePos(suffix as u32),
262+
trimmed_part.span.ctxt(),
263+
trimmed_part.span.parent(),
264+
);
265+
trimmed_part.snippet = substr.to_string();
266+
}
267+
trimmed_part
268+
}
269+
}
270+
271+
impl TrimmedSubstitutionPart {
236272
pub fn is_addition(&self, sm: &SourceMap) -> bool {
237273
!self.snippet.is_empty() && !self.replaces_meaningful_content(sm)
238274
}
@@ -260,27 +296,6 @@ impl SubstitutionPart {
260296
sm.span_to_snippet(self.span)
261297
.map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty())
262298
}
263-
264-
/// Try to turn a replacement into an addition when the span that is being
265-
/// overwritten matches either the prefix or suffix of the replacement.
266-
fn trim_trivial_replacements(&mut self, sm: &SourceMap) {
267-
if self.snippet.is_empty() {
268-
return;
269-
}
270-
let Ok(snippet) = sm.span_to_snippet(self.span) else {
271-
return;
272-
};
273-
274-
if let Some((prefix, substr, suffix)) = as_substr(&snippet, &self.snippet) {
275-
self.span = Span::new(
276-
self.span.lo() + BytePos(prefix as u32),
277-
self.span.hi() - BytePos(suffix as u32),
278-
self.span.ctxt(),
279-
self.span.parent(),
280-
);
281-
self.snippet = substr.to_string();
282-
}
283-
}
284299
}
285300

286301
/// Given an original string like `AACC`, and a suggestion like `AABBCC`, try to detect
@@ -310,7 +325,8 @@ impl CodeSuggestion {
310325
pub(crate) fn splice_lines(
311326
&self,
312327
sm: &SourceMap,
313-
) -> Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, ConfusionType)> {
328+
) -> Vec<(String, Vec<TrimmedSubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, ConfusionType)>
329+
{
314330
// For the `Vec<Vec<SubstitutionHighlight>>` value, the first level of the vector
315331
// corresponds to the output snippet's lines, while the second level corresponds to the
316332
// substrings within that line that should be highlighted.
@@ -423,17 +439,20 @@ impl CodeSuggestion {
423439
lines.lines.get(0).and_then(|line0| sf.get_line(line0.line_index));
424440
let mut buf = String::new();
425441

442+
let trimmed_parts = substitution
443+
.parts
444+
.into_iter()
445+
// If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
446+
// suggestion and snippet to look as if we just suggested to add
447+
// `"b"`, which is typically much easier for the user to understand.
448+
.map(|part| part.trim_trivial_replacements(sm))
449+
.collect::<Vec<_>>();
426450
let mut line_highlight = vec![];
427451
// We need to keep track of the difference between the existing code and the added
428452
// or deleted code in order to point at the correct column *after* substitution.
429453
let mut acc = 0;
430454
let mut confusion_type = ConfusionType::None;
431-
for part in &mut substitution.parts {
432-
// If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
433-
// suggestion and snippet to look as if we just suggested to add
434-
// `"b"`, which is typically much easier for the user to understand.
435-
part.trim_trivial_replacements(sm);
436-
455+
for part in &trimmed_parts {
437456
let part_confusion = detect_confusion_type(sm, &part.snippet, part.span);
438457
confusion_type = confusion_type.combine(part_confusion);
439458
let cur_lo = sm.lookup_char_pos(part.span.lo());
@@ -521,7 +540,7 @@ impl CodeSuggestion {
521540
if highlights.iter().all(|parts| parts.is_empty()) {
522541
None
523542
} else {
524-
Some((buf, substitution.parts, highlights, confusion_type))
543+
Some((buf, trimmed_parts, highlights, confusion_type))
525544
}
526545
})
527546
.collect()

tests/ui/error-emitter/trimmed_multiline_suggestion.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ note: function defined here
1414
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -------
1515
help: provide the argument
1616
|
17-
8 | function_with_lots_of_arguments(
18-
9 | variable_name,
19-
10~ /* char */,
20-
11~ variable_name,
17+
6 | function_with_lots_of_arguments(
18+
7 | variable_name,
19+
8 ~ /* char */,
20+
9 ~ variable_name,
2121
|
2222

2323
error: aborting due to 1 previous error

0 commit comments

Comments
 (0)