diff --git a/src/renderer/render.rs b/src/renderer/render.rs
index e8cb5fa..1f5bfc5 100644
--- a/src/renderer/render.rs
+++ b/src/renderer/render.rs
@@ -1774,7 +1774,10 @@ fn emit_suggestion_default(
// too bad to begin with, so we side-step that issue here.
for (i, line) in snippet.lines().enumerate() {
let line = normalize_whitespace(line);
- let row = row_num - 2 - (newlines - i - 1);
+ // Going lower than buffer_offset (+ 1) would mean
+ // overwriting existing content in the buffer
+ let min_row = buffer_offset + usize::from(!matches_previous_suggestion);
+ let row = (row_num - 2 - (newlines - i - 1)).max(min_row);
// On the first line, we highlight between the start of the part
// span, and the end of that line.
// On the last line, we highlight between the start of the line, and
diff --git a/tests/color/highlight_duplicated_diff_lines.ascii.term.svg b/tests/color/highlight_duplicated_diff_lines.ascii.term.svg
new file mode 100644
index 0000000..cd3db7b
--- /dev/null
+++ b/tests/color/highlight_duplicated_diff_lines.ascii.term.svg
@@ -0,0 +1,62 @@
+
diff --git a/tests/color/highlight_duplicated_diff_lines.rs b/tests/color/highlight_duplicated_diff_lines.rs
new file mode 100644
index 0000000..4f26e8c
--- /dev/null
+++ b/tests/color/highlight_duplicated_diff_lines.rs
@@ -0,0 +1,75 @@
+use annotate_snippets::{renderer::DecorStyle, AnnotationKind, Level, Patch, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+ // https://github.com/rust-lang/rust/blob/4b94758d2ba7d0ef71ccf5fde29ce4bc5d6fe2a4/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.rs
+
+ let source = r#"struct Thingie;
+
+impl Thingie {
+ pub(crate) fn new(
+ _a: String,
+ _b: String,
+ _c: String,
+ _d: String,
+ _e: String,
+ _f: String,
+ ) -> Self {
+ unimplemented!()
+ }
+}
+
+fn main() {
+ let foo = Thingie::new(
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ );
+}"#;
+
+ let path = "$DIR/wrong-highlight-span-extra-arguments-147070.rs";
+
+ let report = &[
+ Level::ERROR
+ .primary_title("this function takes 6 arguments but 7 arguments were supplied")
+ .id("E0061")
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .annotation(
+ AnnotationKind::Context
+ .span(429..445)
+ .label("unexpected argument #7 of type `String`"),
+ )
+ .annotation(AnnotationKind::Primary.span(251..263)),
+ ),
+ Level::NOTE
+ .secondary_title("associated function defined here")
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .annotation(AnnotationKind::Primary.span(50..53)),
+ ),
+ Level::HELP
+ .secondary_title("remove the extra argument")
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .patch(Patch::new(419..445, "")),
+ ),
+ ];
+
+ let expected_ascii = file!["highlight_duplicated_diff_lines.ascii.term.svg": TermSvg];
+ let renderer = Renderer::styled();
+ assert_data_eq!(renderer.render(report), expected_ascii);
+
+ let expected_unicode = file!["highlight_duplicated_diff_lines.unicode.term.svg": TermSvg];
+ let renderer = renderer.decor_style(DecorStyle::Unicode);
+ assert_data_eq!(renderer.render(report), expected_unicode);
+}
diff --git a/tests/color/highlight_duplicated_diff_lines.unicode.term.svg b/tests/color/highlight_duplicated_diff_lines.unicode.term.svg
new file mode 100644
index 0000000..5ae70d1
--- /dev/null
+++ b/tests/color/highlight_duplicated_diff_lines.unicode.term.svg
@@ -0,0 +1,62 @@
+
diff --git a/tests/color/main.rs b/tests/color/main.rs
index f61e113..7d78f4b 100644
--- a/tests/color/main.rs
+++ b/tests/color/main.rs
@@ -9,10 +9,13 @@ mod fold_ann_multiline;
mod fold_bad_origin_line;
mod fold_leading;
mod fold_trailing;
+mod highlight_duplicated_diff_lines;
mod highlight_source;
mod issue_9;
mod multiline_removal_suggestion;
mod multiple_annotations;
+mod multiple_highlight_duplicated;
+mod multiple_multiline_removal;
mod primary_title_second_group;
mod simple;
mod strip_line;
diff --git a/tests/color/multiple_highlight_duplicated.ascii.term.svg b/tests/color/multiple_highlight_duplicated.ascii.term.svg
new file mode 100644
index 0000000..a38c0fa
--- /dev/null
+++ b/tests/color/multiple_highlight_duplicated.ascii.term.svg
@@ -0,0 +1,78 @@
+
diff --git a/tests/color/multiple_highlight_duplicated.rs b/tests/color/multiple_highlight_duplicated.rs
new file mode 100644
index 0000000..625584d
--- /dev/null
+++ b/tests/color/multiple_highlight_duplicated.rs
@@ -0,0 +1,93 @@
+use annotate_snippets::{renderer::DecorStyle, AnnotationKind, Level, Patch, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+ let source = r#"struct Thingie;
+
+impl Thingie {
+ pub(crate) fn new(
+ _a: String,
+ _b: String,
+ _c: String,
+ _d: String,
+ _e: String,
+ _f: String,
+ ) -> Self {
+ unimplemented!()
+ }
+}
+
+fn main() {
+ let foo = Thingie::new(
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ );
+}"#;
+
+ let path = "$DIR/wrong-highlight-span-extra-arguments-147070.rs";
+
+ let report = &[
+ Level::ERROR
+ .primary_title("this function takes 6 arguments but 7 arguments were supplied")
+ .id("E0061")
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .annotation(
+ AnnotationKind::Context
+ .span(429..445)
+ .label("unexpected argument #7 of type `String`"),
+ )
+ .annotation(AnnotationKind::Primary.span(251..263)),
+ ),
+ Level::NOTE
+ .secondary_title("associated function defined here")
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .annotation(AnnotationKind::Primary.span(50..53)),
+ ),
+ Level::HELP
+ .secondary_title("remove the extra argument")
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .patch(Patch::new(419..445, "")),
+ )
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .patch(Patch::new(266..292, "")),
+ )
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .patch(Patch::new(289..315, "")),
+ )
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .patch(Patch::new(403..420, "")),
+ )
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .patch(Patch::new(419..445, "")),
+ ),
+ ];
+
+ let expected_ascii = file!["multiple_highlight_duplicated.ascii.term.svg": TermSvg];
+ let renderer = Renderer::styled();
+ assert_data_eq!(renderer.render(report), expected_ascii);
+
+ let expected_unicode = file!["multiple_highlight_duplicated.unicode.term.svg": TermSvg];
+ let renderer = renderer.decor_style(DecorStyle::Unicode);
+ assert_data_eq!(renderer.render(report), expected_unicode);
+}
diff --git a/tests/color/multiple_highlight_duplicated.unicode.term.svg b/tests/color/multiple_highlight_duplicated.unicode.term.svg
new file mode 100644
index 0000000..8bcafa2
--- /dev/null
+++ b/tests/color/multiple_highlight_duplicated.unicode.term.svg
@@ -0,0 +1,78 @@
+
diff --git a/tests/color/multiple_multiline_removal.ascii.term.svg b/tests/color/multiple_multiline_removal.ascii.term.svg
new file mode 100644
index 0000000..ac3afa1
--- /dev/null
+++ b/tests/color/multiple_multiline_removal.ascii.term.svg
@@ -0,0 +1,114 @@
+
diff --git a/tests/color/multiple_multiline_removal.rs b/tests/color/multiple_multiline_removal.rs
new file mode 100644
index 0000000..ea38a36
--- /dev/null
+++ b/tests/color/multiple_multiline_removal.rs
@@ -0,0 +1,93 @@
+use annotate_snippets::{
+ renderer::DecorStyle, AnnotationKind, Level, Origin, Padding, Patch, Renderer, Snippet,
+};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+ // https://github.com/rust-lang/rust/blob/4b94758d2ba7d0ef71ccf5fde29ce4bc5d6fe2a4/tests/ui/suggestions/multi-suggestion.rs
+
+ let source = r#"#![allow(dead_code)]
+struct U {
+ wtf: Option>>,
+ x: T,
+}
+fn main() {
+ U {
+ wtf: Some(Box(U {
+ wtf: None,
+ x: (),
+ })),
+ x: ()
+ };
+ let _ = std::collections::HashMap();
+ let _ = std::collections::HashMap {};
+ let _ = Box {};
+}
+"#;
+ let path = "$DIR/suggest-box-new.rs";
+ let secondary_path = "$SRC_DIR/alloc/src/boxed.rs";
+
+ let report = &[
+ Level::ERROR
+ .primary_title("cannot initialize a tuple struct which contains private fields")
+ .id("E0423")
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .annotation(AnnotationKind::Primary.span(114..117)),
+ ),
+ Level::NOTE
+ .secondary_title("constructor is not visible here due to private fields")
+ .element(Origin::path(secondary_path).line(234).char_column(2))
+ .element(Padding)
+ .element(Level::NOTE.message("private field"))
+ .element(Padding)
+ .element(Level::NOTE.message("private field")),
+ Level::HELP
+ .secondary_title(
+ "you might have meant to use an associated function to build this type",
+ )
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .patch(Patch::new(117..174, "::new(_)")),
+ )
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .patch(Patch::new(117..174, "::new_uninit()")),
+ )
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .patch(Patch::new(117..174, "::new_zeroed()")),
+ )
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .patch(Patch::new(117..174, "::new_in(_, _)")),
+ )
+ .element(Level::NOTE.no_name().message("and 12 other candidates")),
+ Level::HELP
+ .secondary_title("consider using the `Default` trait")
+ .element(
+ Snippet::source(source)
+ .path(path)
+ .patch(Patch::new(114..114, "<"))
+ .patch(Patch::new(
+ 117..174,
+ " as std::default::Default>::default()",
+ )),
+ ),
+ ];
+
+ let expected_ascii = file!["multiple_multiline_removal.ascii.term.svg": TermSvg];
+ let renderer = Renderer::styled();
+ assert_data_eq!(renderer.render(report), expected_ascii);
+
+ let expected_unicode = file!["multiple_multiline_removal.unicode.term.svg": TermSvg];
+ let renderer = renderer.decor_style(DecorStyle::Unicode);
+ assert_data_eq!(renderer.render(report), expected_unicode);
+}
diff --git a/tests/color/multiple_multiline_removal.unicode.term.svg b/tests/color/multiple_multiline_removal.unicode.term.svg
new file mode 100644
index 0000000..ce8306d
--- /dev/null
+++ b/tests/color/multiple_multiline_removal.unicode.term.svg
@@ -0,0 +1,114 @@
+
diff --git a/typos.toml b/typos.toml
new file mode 100644
index 0000000..708a9e1
--- /dev/null
+++ b/typos.toml
@@ -0,0 +1,2 @@
+[default.extend-words]
+Thingie = "Thingie"