Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/rustc_errors/src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2154,11 +2154,11 @@ impl HumanEmitter {

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

let line_start = sm.lookup_char_pos(parts[0].span.lo()).line;
let line_start = sm.lookup_char_pos(parts[0].original_span.lo()).line;
let mut lines = complete.lines();
if lines.clone().next().is_none() {
// Account for a suggestion to completely remove a line(s) with whitespace (#94192).
let line_end = sm.lookup_char_pos(parts[0].span.hi()).line;
let line_end = sm.lookup_char_pos(parts[0].original_span.hi()).line;
for line in line_start..=line_end {
self.draw_line_num(
&mut buffer,
Expand Down
71 changes: 46 additions & 25 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,13 @@ pub struct SubstitutionPart {
pub snippet: String,
}

#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
pub struct TrimmedSubstitutionPart {
pub original_span: Span,
pub span: Span,
pub snippet: String,
}

/// Used to translate between `Span`s and byte positions within a single output line in highlighted
/// code of structured suggestions.
#[derive(Debug, Clone, Copy)]
Expand All @@ -233,6 +240,35 @@ pub(crate) struct SubstitutionHighlight {
}

impl SubstitutionPart {
/// Try to turn a replacement into an addition when the span that is being
/// overwritten matches either the prefix or suffix of the replacement.
fn trim_trivial_replacements(self, sm: &SourceMap) -> TrimmedSubstitutionPart {
let mut trimmed_part = TrimmedSubstitutionPart {
original_span: self.span,
span: self.span,
snippet: self.snippet,
};
if trimmed_part.snippet.is_empty() {
return trimmed_part;
}
let Ok(snippet) = sm.span_to_snippet(trimmed_part.span) else {
return trimmed_part;
};

if let Some((prefix, substr, suffix)) = as_substr(&snippet, &trimmed_part.snippet) {
trimmed_part.span = Span::new(
trimmed_part.span.lo() + BytePos(prefix as u32),
trimmed_part.span.hi() - BytePos(suffix as u32),
trimmed_part.span.ctxt(),
trimmed_part.span.parent(),
);
trimmed_part.snippet = substr.to_string();
}
trimmed_part
}
}

impl TrimmedSubstitutionPart {
pub fn is_addition(&self, sm: &SourceMap) -> bool {
!self.snippet.is_empty() && !self.replaces_meaningful_content(sm)
}
Expand Down Expand Up @@ -260,27 +296,6 @@ impl SubstitutionPart {
sm.span_to_snippet(self.span)
.map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty())
}

/// Try to turn a replacement into an addition when the span that is being
/// overwritten matches either the prefix or suffix of the replacement.
fn trim_trivial_replacements(&mut self, sm: &SourceMap) {
if self.snippet.is_empty() {
return;
}
let Ok(snippet) = sm.span_to_snippet(self.span) else {
return;
};

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
Expand Down Expand Up @@ -310,7 +325,8 @@ impl CodeSuggestion {
pub(crate) fn splice_lines(
&self,
sm: &SourceMap,
) -> Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, ConfusionType)> {
) -> Vec<(String, Vec<TrimmedSubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, ConfusionType)>
{
// For the `Vec<Vec<SubstitutionHighlight>>` value, the first level of the vector
// corresponds to the output snippet's lines, while the second level corresponds to the
// substrings within that line that should be highlighted.
Expand Down Expand Up @@ -428,12 +444,17 @@ impl CodeSuggestion {
// or deleted code in order to point at the correct column *after* substitution.
let mut acc = 0;
let mut confusion_type = ConfusionType::None;
for part in &mut substitution.parts {

let trimmed_parts = substitution
.parts
.into_iter()
// If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
// suggestion and snippet to look as if we just suggested to add
// `"b"`, which is typically much easier for the user to understand.
part.trim_trivial_replacements(sm);
.map(|part| part.trim_trivial_replacements(sm))
.collect::<Vec<_>>();

for part in &trimmed_parts {
let part_confusion = detect_confusion_type(sm, &part.snippet, part.span);
confusion_type = confusion_type.combine(part_confusion);
let cur_lo = sm.lookup_char_pos(part.span.lo());
Expand Down Expand Up @@ -521,7 +542,7 @@ impl CodeSuggestion {
if highlights.iter().all(|parts| parts.is_empty()) {
None
} else {
Some((buf, substitution.parts, highlights, confusion_type))
Some((buf, trimmed_parts, highlights, confusion_type))
}
})
.collect()
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/error-emitter/trimmed_multiline_suggestion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//@ compile-flags: -Z ui-testing=no
fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {}

fn main() {
let variable_name = 42;
function_with_lots_of_arguments(
variable_name,
variable_name,
variable_name,
variable_name,
variable_name,
);
//~^^^^^^^ ERROR this function takes 6 arguments but 5 arguments were supplied [E0061]
}
25 changes: 25 additions & 0 deletions tests/ui/error-emitter/trimmed_multiline_suggestion.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
error[E0061]: this function takes 6 arguments but 5 arguments were supplied
--> $DIR/trimmed_multiline_suggestion.rs:6:5
|
6 | function_with_lots_of_arguments(
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7 | variable_name,
8 | variable_name,
| ------------- argument #2 of type `char` is missing
|
note: function defined here
--> $DIR/trimmed_multiline_suggestion.rs:2:4
|
2 | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -------
help: provide the argument
|
6 | function_with_lots_of_arguments(
7 | variable_name,
8 ~ /* char */,
9 ~ variable_name,
|

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0061`.
Loading