Skip to content

Commit 1bb91de

Browse files
authored
Rollup merge of #147785 - relaxcn:emitter_sugg_line, r=petrochenkov
fix incorrect line number when building trimmed multi-line suggestions While fixing the issue rust-lang/rust-clippy#15883 from `rust-clippy`, I tracked it down to a problem in `rustc` where line numbers were incorrect when building trimmed multi-line suggestions.
2 parents 5be6c6f + ad67c9d commit 1bb91de

File tree

4 files changed

+87
-27
lines changed

4 files changed

+87
-27
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: 46 additions & 25 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.
@@ -428,12 +444,17 @@ impl CodeSuggestion {
428444
// or deleted code in order to point at the correct column *after* substitution.
429445
let mut acc = 0;
430446
let mut confusion_type = ConfusionType::None;
431-
for part in &mut substitution.parts {
447+
448+
let trimmed_parts = substitution
449+
.parts
450+
.into_iter()
432451
// If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
433452
// suggestion and snippet to look as if we just suggested to add
434453
// `"b"`, which is typically much easier for the user to understand.
435-
part.trim_trivial_replacements(sm);
454+
.map(|part| part.trim_trivial_replacements(sm))
455+
.collect::<Vec<_>>();
436456

457+
for part in &trimmed_parts {
437458
let part_confusion = detect_confusion_type(sm, &part.snippet, part.span);
438459
confusion_type = confusion_type.combine(part_confusion);
439460
let cur_lo = sm.lookup_char_pos(part.span.lo());
@@ -521,7 +542,7 @@ impl CodeSuggestion {
521542
if highlights.iter().all(|parts| parts.is_empty()) {
522543
None
523544
} else {
524-
Some((buf, substitution.parts, highlights, confusion_type))
545+
Some((buf, trimmed_parts, highlights, confusion_type))
525546
}
526547
})
527548
.collect()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//@ compile-flags: -Z ui-testing=no
2+
fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {}
3+
4+
fn main() {
5+
let variable_name = 42;
6+
function_with_lots_of_arguments(
7+
variable_name,
8+
variable_name,
9+
variable_name,
10+
variable_name,
11+
variable_name,
12+
);
13+
//~^^^^^^^ ERROR this function takes 6 arguments but 5 arguments were supplied [E0061]
14+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0061]: this function takes 6 arguments but 5 arguments were supplied
2+
--> $DIR/trimmed_multiline_suggestion.rs:6:5
3+
|
4+
6 | function_with_lots_of_arguments(
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7 | variable_name,
7+
8 | variable_name,
8+
| ------------- argument #2 of type `char` is missing
9+
|
10+
note: function defined here
11+
--> $DIR/trimmed_multiline_suggestion.rs:2:4
12+
|
13+
2 | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {}
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -------
15+
help: provide the argument
16+
|
17+
6 | function_with_lots_of_arguments(
18+
7 | variable_name,
19+
8 ~ /* char */,
20+
9 ~ variable_name,
21+
|
22+
23+
error: aborting due to 1 previous error
24+
25+
For more information about this error, try `rustc --explain E0061`.

0 commit comments

Comments
 (0)