Change:
Given the following code: The current output is: The suggestion looks weird, seeming to suggest removing and replacing it with a duplicate of the line that follows it. The actual fixup'd code (via ) isn't actually broken. does the change as expected, removing just the . So this leads me to believe this is instead just a bug in the code that renders suggestions. spotted this in but this is not related to that PR. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
cc -- I think you wrote the code that generates these diffs?
I did. I guess what we could do is detect this case in particular and show only the part. Alternatively, that should instead be .
claim Perhaps I'll try this out :) Which part of the code would need to be changed to only show the line?
I bet the code is somewhere around here haven't looked much into the rendering, but a theory:  might be wrong for removal suggestions that end in . Otherwise maybe we just need to suppress emitting on the line following any time we render a removal suggestion ending in in .
Thanks for the suggestion! I tried trimming newlines before calling , which didn't seem to help, so I think it may be elsewhere. Some results from investigating: The span for the substitution with is 2:5 (line 2, col. 5) through 3:5. So the source range doesn't actually end in a newline; it ends in spaces for indentation on the next line. Then I think line 3 is computed as the line to display as being (which makes sense given the output), though I'm still not sure why.
claim
Hey! I took a look at this and I have found some things, but I don't see a clear path to fix it. was right that the span seems to be wrong. It takes the line with the ampersand, but also the indentation of the next line. The code that produces that span is . After experimenting a bit, I understood why the span is like that. It tries to remove the part of a expression that comes before a expression. We can't just remove one character (the &), because the borrow could also be mutable (). So I understand this is working as expected and shouldn't be touched, right? The problem is, that even using that span, the suggestion we are getting is wrong. I would expect something like this: Maybe that isn't ideal, but it makes sense (at least to me), as the new line takes the whitespace from the first one. However, this doesn't happen and I'm having difficulties finding where the error is in the code. I have found that in takes the value , but I'm not sure if this is the correct value or not. Not really sure about what is supposed to be. So, to sum up: Should I "fix" the span or the display of that span? What is supposed to hold?
I think that fixing the rendering of the span is the appropriate solution. We could infer for example that the only changes that are additions (rendered with a + next to the line number) are purely whitespace, and then not render it at all. I think that would convey the "delete this line" meaning well enough.
I'm not sure I understand. The addition that appears in the diagnostic isn't only whitespace. If we remove the whole span, the line 2 is another almost entirely different line (in fact, the only thing that doesn't change from the compiler point of view is precisely the whitespace). I think the root of the problem is that right now the compiler has trouble when rendering removals that span more than one line. The problem is even worse if there are more lines involved: I don't think this can be fixed without restructuring at least part of the rendering, but I've found the code to be really difficult to understand (I guess there are lots of edge cases for things like this).
the patch rendering of the suggested change would have been because the span that is being removed (and hence the lines that are being modified) is which puts the start of the directly after the whitespace that comes before the , instead of removing the lines with the and the (which is what a human would do). The span is correct. Calculating that you can get the same effect by removing lines 2 and 3 when preparing the suggestion is hard (and too bespoke, the same logic would have to be written in multiple places). Detecting cases like these when rendering the suggestion will work for every suggestion and likely be easier to implement. Does this make sense?
Yes! That makes sense. The problem is that what is actually happening in the code is that it is inferring that it should substitute the line with , without showing that the next line should be removed. And with the way that the rendering code is structured, I'm having trouble seeing how to fix that. I'll keep looking. Thanks!

Discussion:
&mut buffer, &mut row_num, &Vec::new(), <del> p, </del> <ins> p + line_start, </ins> l, <del> line_start, </del> show_code_change, max_line_num_len, &file_lines,

&mut buffer, &mut row_num, highlight_parts, <del> line_pos, </del> <ins> line_pos + line_start, </ins> line, <del> line_start, </del> show_code_change, max_line_num_len, &file_lines,

buffer: &mut StyledBuffer, row_num: &mut usize, highlight_parts: &Vec<SubstitutionHighlight>, <del> line_pos: usize, line: &str, line_start: usize, </del> <ins> line_num: usize, line_to_add: &str, </ins> show_code_change: DisplaySuggestion, max_line_num_len: usize, file_lines: &FileLines, is_multiline: bool, ) { <del> // Print the span column to avoid confusion buffer.puts(*row_num, 0, &self.maybe_anonymized(line_start + line_pos), Style::LineNumber); </del> if let DisplaySuggestion::Diff = show_code_change { <del> // Add the line number for both addition and removal to drive the point home. // // N - fn foo<A: T>(bar: A) { // N + fn foo(bar: impl T) { buffer.puts( *row_num - 1, 0, &self.maybe_anonymized(line_start + line_pos), Style::LineNumber, ); buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal); buffer.puts( *row_num - 1, max_line_num_len + 3, &normalize_whitespace( &file_lines.file.get_line(file_lines.lines[line_pos].line_index).unwrap(), ), Style::NoStyle, ); buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); </del> <ins> // We need to print more than one line if the span we need to remove is multiline. // For more info: https://github.com/rust-lang/rust/issues/92741 let lines_to_remove = file_lines.lines.iter().take(file_lines.lines.len() - 1); for (index, line_to_remove) in lines_to_remove.enumerate() { buffer.puts( *row_num - 1, 0, &self.maybe_anonymized(line_num + index), Style::LineNumber, ); buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal); let line = normalize_whitespace( &file_lines.file.get_line(line_to_remove.line_index).unwrap(), ); buffer.puts(*row_num - 1, max_line_num_len + 3, &line, Style::NoStyle); *row_num += 1; } // If the last line is exactly equal to the line we need to add, we can skip both of them. // This allows us to avoid output like the following: // 2 - & // 2 + if true { true } else { false } // 3 - if true { true } else { false } // If those lines aren't equal, we print their diff let last_line_index = file_lines.lines[file_lines.lines.len() - 1].line_index; let last_line = &file_lines.file.get_line(last_line_index).unwrap(); if last_line != line_to_add { buffer.puts( *row_num - 1, 0, &self.maybe_anonymized(line_num + file_lines.lines.len() - 1), Style::LineNumber, ); buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal); buffer.puts( *row_num - 1, max_line_num_len + 3, &normalize_whitespace(last_line), Style::NoStyle, ); buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } else { *row_num -= 2; } </ins> } else if is_multiline { <ins> buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); </ins> match &highlight_parts[..] { <del> [SubstitutionHighlight { start: 0, end }] if *end == line.len() => { </del> <ins> [SubstitutionHighlight { start: 0, end }] if *end == line_to_add.len() => { </ins> buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); } [] => {

buffer.puts(*row_num, max_line_num_len + 1, "~ ", Style::Addition); } } <ins> buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); </ins> } else { <ins> buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); </ins> draw_col_separator(buffer, *row_num, max_line_num_len + 1); <ins> buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); </ins> } <del> // print the suggestion buffer.append(*row_num, &normalize_whitespace(line), Style::NoStyle); </del> // Colorize addition/replacements with green. for &SubstitutionHighlight { start, end } in highlight_parts { // Account for tabs when highlighting (#87972). <del> let tabs: usize = line </del> <ins> let tabs: usize = line_to_add </ins> .chars() .take(start) .map(|ch| match ch {

const ENTRY_LIMIT: usize = 1000; // FIXME: The following limits should be reduced eventually. const ROOT_ENTRY_LIMIT: usize = 939; <del> const ISSUES_ENTRY_LIMIT: usize = 1998; </del> <ins> const ISSUES_ENTRY_LIMIT: usize = 2001; </ins> fn check_entries(path: &Path, bad: &mut bool) { for dir in Walk::new(&path.join("ui")) {

<ins> // run-rustfix fn main() {} fn _foo() -> bool { if true { true } else { false } } fn _bar() -> bool { if true { true } else { false } } fn _baz() -> bool { if true { true } else { false } } </ins>

<ins> // run-rustfix fn main() {} fn _foo() -> bool { &  //~ ERROR 4:5: 6:36: mismatched types [E0308] mut if true { true } else { false } } fn _bar() -> bool { &  //~ ERROR 10:5: 11:40: mismatched types [E0308] mut if true { true } else { false } } fn _baz() -> bool { & mut //~ ERROR 15:5: 16:36: mismatched types [E0308] if true { true } else { false } } </ins>

<ins> error[E0308]: mismatched types --> $DIR/issue-92741.rs:4:5 | LL |   fn _foo() -> bool { |                ---- expected `bool` because of return type LL | /     & LL | |     mut LL | |     if true { true } else { false } | |___________________________________^ expected `bool`, found `&mut bool` | help: consider removing the borrow | LL -     & LL -     mut | error[E0308]: mismatched types --> $DIR/issue-92741.rs:10:5 | LL |   fn _bar() -> bool { |                ---- expected `bool` because of return type LL | /     & LL | |     mut if true { true } else { false } | |_______________________________________^ expected `bool`, found `&mut bool` | help: consider removing the borrow | LL -     & LL -     mut if true { true } else { false } LL +     if true { true } else { false } | error[E0308]: mismatched types --> $DIR/issue-92741.rs:15:5 | LL |   fn _baz() -> bool { |                ---- expected `bool` because of return type LL | /     & mut LL | |     if true { true } else { false } | |___________________________________^ expected `bool`, found `&mut bool` | help: consider removing the borrow | LL -     & mut | error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. </ins>