diff --git a/src/renderer/source_map.rs b/src/renderer/source_map.rs index e2e4b61..44b773b 100644 --- a/src/renderer/source_map.rs +++ b/src/renderer/source_map.rs @@ -379,6 +379,19 @@ impl<'a> SourceMap<'a> { } line_count } + + let source_len = self.source.len(); + if let Some(bigger) = patches.iter().find_map(|x| { + // Allow patching one past the last character in the source. + if source_len + 1 < x.span.end { + Some(&x.span) + } else { + None + } + }) { + panic!("Patch span `{bigger:?}` is beyond the end of buffer `{source_len}`") + } + // Assumption: all spans are in the same file, and all spans // are disjoint. Sort in ascending order. patches.sort_by_key(|p| p.span.start); diff --git a/tests/formatter.rs b/tests/formatter.rs index eca0b82..1d21d13 100644 --- a/tests/formatter.rs +++ b/tests/formatter.rs @@ -2635,3 +2635,121 @@ fn empty_span_start_line() { let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input), expected); } + +#[test] +fn suggestion_span_one_bigger_than_source() { + let snippet_source = r#"#![allow(unused)] +fn main() { +[1, 2, 3].into_iter().for_each(|n| { *n; }); +} +"#; + + let suggestion_source = r#"[1, 2, 3].into_iter().for_each(|n| { *n; }); +"#; + + let long_title1 ="this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021"; + let long_title2 = "for more information, see "; + let long_title3 = "or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value"; + + let input = &[ + Group::with_title(Level::WARNING.title(long_title1)) + .element( + Snippet::source(snippet_source) + .path("lint_example.rs") + .annotation(AnnotationKind::Primary.span(40..49)), + ) + .element(Level::WARNING.message("this changes meaning in Rust 2021")) + .element(Level::NOTE.message(long_title2)) + .element(Level::NOTE.message("`#[warn(array_into_iter)]` on by default")), + Group::with_title( + Level::HELP.title("use `.iter()` instead of `.into_iter()` to avoid ambiguity"), + ) + .element( + Snippet::source(suggestion_source) + .path("lint_example.rs") + .line_start(3) + .patch(Patch::new(10..19, "iter")), + ), + Group::with_title(Level::HELP.title(long_title3)).element( + Snippet::source(suggestion_source) + .path("lint_example.rs") + .line_start(3) + .patch(Patch::new( + suggestion_source.len() + 1..suggestion_source.len() + 1, + "IntoIterator::into_iter(", + )), + ), + ]; + + let expected = str![[r#" +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 + --> lint_example.rs:3:11 + | +3 | [1, 2, 3].into_iter().for_each(|n| { *n; }); + | ^^^^^^^^^ + | + = warning: this changes meaning in Rust 2021 + = note: for more information, see + = note: `#[warn(array_into_iter)]` on by default +help: use `.iter()` instead of `.into_iter()` to avoid ambiguity + | +3 - [1, 2, 3].into_iter().for_each(|n| { *n; }); +3 + [1, 2, 3].iter().for_each(|n| { *n; }); + | +help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value + | +3 | IntoIterator::into_iter( + | +"#]]; + let renderer = Renderer::plain(); + assert_data_eq!(renderer.render(input), expected); +} + +#[test] +#[should_panic = "Patch span `47..47` is beyond the end of buffer `45`"] +fn suggestion_span_bigger_than_source() { + let snippet_source = r#"#![allow(unused)] +fn main() { +[1, 2, 3].into_iter().for_each(|n| { *n; }); +} +"#; + let suggestion_source = r#"[1, 2, 3].into_iter().for_each(|n| { *n; }); +"#; + + let long_title1 ="this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021"; + let long_title2 = "for more information, see "; + let long_title3 = "or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value"; + + let input = &[ + Group::with_title(Level::WARNING.title(long_title1)) + .element( + Snippet::source(snippet_source) + .path("lint_example.rs") + .annotation(AnnotationKind::Primary.span(40..49)), + ) + .element(Level::WARNING.message("this changes meaning in Rust 2021")) + .element(Level::NOTE.message(long_title2)) + .element(Level::NOTE.message("`#[warn(array_into_iter)]` on by default")), + Group::with_title( + Level::HELP.title("use `.iter()` instead of `.into_iter()` to avoid ambiguity"), + ) + .element( + Snippet::source(suggestion_source) + .path("lint_example.rs") + .line_start(3) + .patch(Patch::new(10..19, "iter")), + ), + Group::with_title(Level::HELP.title(long_title3)).element( + Snippet::source(suggestion_source) + .path("lint_example.rs") + .line_start(3) + .patch(Patch::new( + suggestion_source.len() + 2..suggestion_source.len() + 2, + "IntoIterator::into_iter(", + )), + ), + ]; + + let renderer = Renderer::plain(); + renderer.render(input); +}