From 5b471407e9fa2dd8f44a9223b46d3d0bf7bf6c67 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Wed, 22 Oct 2025 02:37:46 -0600 Subject: [PATCH 1/3] test: Improve clarity of sugg span bigger that source --- tests/formatter.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/formatter.rs b/tests/formatter.rs index 5065f19..3d2c3ac 100644 --- a/tests/formatter.rs +++ b/tests/formatter.rs @@ -3617,7 +3617,7 @@ fn main() { .line_start(3) .patch(Patch::new( suggestion_source.len() + 1..suggestion_source.len() + 1, - "IntoIterator::into_iter(", + " // Span after line end", )), ), ]; @@ -3639,7 +3639,7 @@ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value | -3 | IntoIterator::into_iter( +3 | // Span after line end | "#]]; let renderer = Renderer::plain(); @@ -3662,7 +3662,7 @@ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity ╰╴ help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value ╭╴ -3 │ IntoIterator::into_iter( +3 │ // Span after line end ╰╴ "#]]; let renderer = renderer.decor_style(DecorStyle::Unicode); @@ -3709,7 +3709,7 @@ fn main() { .line_start(3) .patch(Patch::new( suggestion_source.len() + 2..suggestion_source.len() + 2, - "IntoIterator::into_iter(", + " // Span after line end", )), ), ]; From de0439765e9fd137e8e32f89ccf31f9de4798ec6 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Wed, 22 Oct 2025 09:12:20 -0600 Subject: [PATCH 2/3] test: Add test for suggestion span equaling EOL and EOF --- tests/formatter.rs | 178 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/tests/formatter.rs b/tests/formatter.rs index 3d2c3ac..4706b02 100644 --- a/tests/formatter.rs +++ b/tests/formatter.rs @@ -3577,6 +3577,184 @@ fn empty_span_start_line() { assert_data_eq!(renderer.render(input), expected_unicode); } +#[test] +fn suggestion_span_line_end() { + let source = r#"#![allow(unused)] +fn main() { +[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 = &[ + Level::WARNING + .primary_title(long_title1) + .element( + Snippet::source(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")), + Level::HELP + .secondary_title("use `.iter()` instead of `.into_iter()` to avoid ambiguity") + .element( + Snippet::source(source) + .path("lint_example.rs") + .line_start(3) + .patch(Patch::new(40..49, "iter")), + ), + Level::HELP.secondary_title(long_title3).element( + Snippet::source(source) + .path("lint_example.rs") + .line_start(3) + .patch(Patch::new(74..74, " // Span after line end")), + ), + ]; + + let expected_ascii = 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 + | +5 - [1, 2, 3].into_iter().for_each(|n| { *n; }); +5 + [1, 2, 3].iter().for_each(|n| { *n; }); + | +help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value + | +5 | [1, 2, 3].into_iter().for_each(|n| { *n; }); // Span after line end + | ++++++++++++++++++++++ +"#]]; + let renderer = Renderer::plain(); + assert_data_eq!(renderer.render(input), expected_ascii); + + let expected_unicode = 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 + ╭╴ +5 - [1, 2, 3].into_iter().for_each(|n| { *n; }); +5 + [1, 2, 3].iter().for_each(|n| { *n; }); + ╰╴ +help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value + ╭╴ +5 │ [1, 2, 3].into_iter().for_each(|n| { *n; }); // Span after line end + ╰╴ ++++++++++++++++++++++ +"#]]; + let renderer = renderer.decor_style(DecorStyle::Unicode); + assert_data_eq!(renderer.render(input), expected_unicode); +} + +#[test] +fn suggestion_span_source_end() { + 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 = &[ + Level::WARNING + .primary_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")), + Level::HELP + .secondary_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")), + ), + Level::HELP.secondary_title(long_title3).element( + Snippet::source(suggestion_source) + .path("lint_example.rs") + .line_start(3) + .patch(Patch::new( + suggestion_source.len()..suggestion_source.len(), + " // Span after line end", + )), + ), + ]; + + let expected_ascii = 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 | // Span after line end + | +"#]]; + let renderer = Renderer::plain(); + assert_data_eq!(renderer.render(input), expected_ascii); + + let expected_unicode = 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 │ // Span after line end + ╰╴ +"#]]; + let renderer = renderer.decor_style(DecorStyle::Unicode); + assert_data_eq!(renderer.render(input), expected_unicode); +} + #[test] fn suggestion_span_one_bigger_than_source() { let snippet_source = r#"#![allow(unused)] From 3a561d4d307119f1d61c6157fd468deee8303245 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Tue, 21 Oct 2025 11:34:28 -0600 Subject: [PATCH 3/3] fix: Show full line when patch span is points to line end --- src/renderer/source_map.rs | 5 +++++ tests/formatter.rs | 16 ++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/renderer/source_map.rs b/src/renderer/source_map.rs index 79bf2d6..95b9030 100644 --- a/src/renderer/source_map.rs +++ b/src/renderer/source_map.rs @@ -130,6 +130,11 @@ impl<'a> SourceMap<'a> { } lines.push(line_info); } + + if lines.is_empty() && !self.lines.is_empty() { + lines.push(self.lines.last().unwrap()); + } + lines } diff --git a/tests/formatter.rs b/tests/formatter.rs index 4706b02..3bdeb6c 100644 --- a/tests/formatter.rs +++ b/tests/formatter.rs @@ -3725,8 +3725,8 @@ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value | -3 | // Span after line end - | +3 | [1, 2, 3].into_iter().for_each(|n| { *n; }); // Span after line end + | ++++++++++++++++++++++ "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input), expected_ascii); @@ -3748,8 +3748,8 @@ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity ╰╴ help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value ╭╴ -3 │ // Span after line end - ╰╴ +3 │ [1, 2, 3].into_iter().for_each(|n| { *n; }); // Span after line end + ╰╴ ++++++++++++++++++++++ "#]]; let renderer = renderer.decor_style(DecorStyle::Unicode); assert_data_eq!(renderer.render(input), expected_unicode); @@ -3817,8 +3817,8 @@ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value | -3 | // Span after line end - | +3 | [1, 2, 3].into_iter().for_each(|n| { *n; }); // Span after line end + | ++++++++++++++++++++++ "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input), expected_ascii); @@ -3840,8 +3840,8 @@ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity ╰╴ help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value ╭╴ -3 │ // Span after line end - ╰╴ +3 │ [1, 2, 3].into_iter().for_each(|n| { *n; }); // Span after line end + ╰╴ ++++++++++++++++++++++ "#]]; let renderer = renderer.decor_style(DecorStyle::Unicode); assert_data_eq!(renderer.render(input), expected_unicode);