From 16d979fca9e7342407efd50e762bfc47fc9079d1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 15 Sep 2025 21:04:21 -0500 Subject: [PATCH 1/9] fix(manifest): Be more direct in infostring errors --- src/cargo/util/toml/embedded.rs | 6 ++---- .../script/rustc_fixtures/dot-in-infostring-leading.stderr | 2 +- .../rustc_fixtures/dot-in-infostring-non-leading.stderr | 2 +- .../rustc_fixtures/hyphen-in-infostring-leading.stderr | 2 +- .../rustc_fixtures/hyphen-in-infostring-non-leading.stderr | 2 +- .../testsuite/script/rustc_fixtures/infostring-fail.stderr | 2 +- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index dd10ebc3e24..b29bf45691d 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -12,15 +12,13 @@ pub(super) fn expand_manifest(content: &str) -> Result Some(other) => { if let Some(remainder) = other.strip_prefix("cargo,") { return Err(FrontmatterError::new( - format!( - "cargo does not support frontmatter infostring attributes like `{remainder}` at this time" - ), + format!("unsupported frontmatter infostring attributes: `{remainder}`"), source.info_span().unwrap(), )); } else { return Err(FrontmatterError::new( format!( - "frontmatter infostring `{other}` is unsupported by cargo; specify `cargo` for embedding a manifest" + "unsupported frontmatter infostring `{other}`; specify `cargo` for embedding a manifest" ), source.info_span().unwrap(), )); diff --git a/tests/testsuite/script/rustc_fixtures/dot-in-infostring-leading.stderr b/tests/testsuite/script/rustc_fixtures/dot-in-infostring-leading.stderr index cccb080269b..01521f3efa1 100644 --- a/tests/testsuite/script/rustc_fixtures/dot-in-infostring-leading.stderr +++ b/tests/testsuite/script/rustc_fixtures/dot-in-infostring-leading.stderr @@ -1,4 +1,4 @@ -[ERROR] frontmatter infostring `.toml` is unsupported by cargo; specify `cargo` for embedding a manifest +[ERROR] unsupported frontmatter infostring `.toml`; specify `cargo` for embedding a manifest --> script:1:4 | 1 | ---.toml diff --git a/tests/testsuite/script/rustc_fixtures/dot-in-infostring-non-leading.stderr b/tests/testsuite/script/rustc_fixtures/dot-in-infostring-non-leading.stderr index ac684b2eab2..d075c8bb3d1 100644 --- a/tests/testsuite/script/rustc_fixtures/dot-in-infostring-non-leading.stderr +++ b/tests/testsuite/script/rustc_fixtures/dot-in-infostring-non-leading.stderr @@ -1,4 +1,4 @@ -[ERROR] frontmatter infostring `Cargo.toml` is unsupported by cargo; specify `cargo` for embedding a manifest +[ERROR] unsupported frontmatter infostring `Cargo.toml`; specify `cargo` for embedding a manifest --> script:1:4 | 1 | ---Cargo.toml diff --git a/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-leading.stderr b/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-leading.stderr index 2b39032a985..ab78161fd0c 100644 --- a/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-leading.stderr +++ b/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-leading.stderr @@ -1,4 +1,4 @@ -[ERROR] frontmatter infostring `-toml` is unsupported by cargo; specify `cargo` for embedding a manifest +[ERROR] unsupported frontmatter infostring `-toml`; specify `cargo` for embedding a manifest --> script:1:5 | 1 | --- -toml diff --git a/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-non-leading.stderr b/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-non-leading.stderr index a19c79b7def..76838fec241 100644 --- a/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-non-leading.stderr +++ b/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-non-leading.stderr @@ -1,4 +1,4 @@ -[ERROR] frontmatter infostring `Cargo-toml` is unsupported by cargo; specify `cargo` for embedding a manifest +[ERROR] unsupported frontmatter infostring `Cargo-toml`; specify `cargo` for embedding a manifest --> script:1:5 | 1 | --- Cargo-toml diff --git a/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr b/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr index bbd2e32f20d..5bc6438f882 100644 --- a/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr +++ b/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr @@ -1,4 +1,4 @@ -[ERROR] cargo does not support frontmatter infostring attributes like `clippy` at this time +[ERROR] unsupported frontmatter infostring attributes: `clippy` --> script:1:4 | 1 | ---cargo,clippy From 60bcd83510d6444fcad623049816e8ade4116b85 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 15 Sep 2025 21:09:24 -0500 Subject: [PATCH 2/9] fix(frontmatter): Be more direct in unclosed error For comparison, rustc's message is: > error: unclosed frontmatter > --> $DIR/frontmatter-whitespace-2.rs:1:1 > | > LL | / ---cargo > ... | > LL | | > | |_^ > | > note: frontmatter opening here was not closed > --> $DIR/frontmatter-whitespace-2.rs:1:1 > | > LL | ---cargo > | ^^^ --- src/cargo/util/frontmatter.rs | 6 +++--- .../script/rustc_fixtures/frontmatter-whitespace-2.stderr | 2 +- tests/testsuite/script/rustc_fixtures/unclosed-1.stderr | 2 +- tests/testsuite/script/rustc_fixtures/unclosed-2.stderr | 2 +- tests/testsuite/script/rustc_fixtures/unclosed-3.stderr | 2 +- tests/testsuite/script/rustc_fixtures/unclosed-4.stderr | 2 +- tests/testsuite/script/rustc_fixtures/unclosed-5.stderr | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cargo/util/frontmatter.rs b/src/cargo/util/frontmatter.rs index 009fd3726b4..f40c36403fc 100644 --- a/src/cargo/util/frontmatter.rs +++ b/src/cargo/util/frontmatter.rs @@ -80,7 +80,7 @@ impl<'s> ScriptSource<'s> { source.open = Some(open_start..open_end); let Some(info_nl) = input.find_slice("\n") else { return Err(FrontmatterError::new( - format!("no closing `{fence_pattern}` found for frontmatter"), + format!("unclosed frontmatter; expected `{fence_pattern}`"), open_start..open_end, )); }; @@ -109,7 +109,7 @@ impl<'s> ScriptSource<'s> { )); } return Err(FrontmatterError::new( - format!("no closing `{fence_pattern}` found for frontmatter"), + format!("unclosed frontmatter; expected `{fence_pattern}`"), open_start..open_end, )); }; @@ -636,7 +636,7 @@ time="0.1.25" fn main() {} "#, ), - str!["no closing `---` found for frontmatter"], + str!["unclosed frontmatter; expected `---`"], ); } } diff --git a/tests/testsuite/script/rustc_fixtures/frontmatter-whitespace-2.stderr b/tests/testsuite/script/rustc_fixtures/frontmatter-whitespace-2.stderr index f95dd52746b..d567931e305 100644 --- a/tests/testsuite/script/rustc_fixtures/frontmatter-whitespace-2.stderr +++ b/tests/testsuite/script/rustc_fixtures/frontmatter-whitespace-2.stderr @@ -1,4 +1,4 @@ -[ERROR] no closing `---` found for frontmatter +[ERROR] unclosed frontmatter; expected `---` --> script:1:1 | 1 | ---cargo diff --git a/tests/testsuite/script/rustc_fixtures/unclosed-1.stderr b/tests/testsuite/script/rustc_fixtures/unclosed-1.stderr index 31743531ccf..89d0b151d5d 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-1.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-1.stderr @@ -1,4 +1,4 @@ -[ERROR] no closing `----` found for frontmatter +[ERROR] unclosed frontmatter; expected `----` --> script:1:1 | 1 | ----cargo diff --git a/tests/testsuite/script/rustc_fixtures/unclosed-2.stderr b/tests/testsuite/script/rustc_fixtures/unclosed-2.stderr index 31743531ccf..89d0b151d5d 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-2.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-2.stderr @@ -1,4 +1,4 @@ -[ERROR] no closing `----` found for frontmatter +[ERROR] unclosed frontmatter; expected `----` --> script:1:1 | 1 | ----cargo diff --git a/tests/testsuite/script/rustc_fixtures/unclosed-3.stderr b/tests/testsuite/script/rustc_fixtures/unclosed-3.stderr index 31743531ccf..89d0b151d5d 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-3.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-3.stderr @@ -1,4 +1,4 @@ -[ERROR] no closing `----` found for frontmatter +[ERROR] unclosed frontmatter; expected `----` --> script:1:1 | 1 | ----cargo diff --git a/tests/testsuite/script/rustc_fixtures/unclosed-4.stderr b/tests/testsuite/script/rustc_fixtures/unclosed-4.stderr index 31743531ccf..89d0b151d5d 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-4.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-4.stderr @@ -1,4 +1,4 @@ -[ERROR] no closing `----` found for frontmatter +[ERROR] unclosed frontmatter; expected `----` --> script:1:1 | 1 | ----cargo diff --git a/tests/testsuite/script/rustc_fixtures/unclosed-5.stderr b/tests/testsuite/script/rustc_fixtures/unclosed-5.stderr index 31743531ccf..89d0b151d5d 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-5.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-5.stderr @@ -1,4 +1,4 @@ -[ERROR] no closing `----` found for frontmatter +[ERROR] unclosed frontmatter; expected `----` --> script:1:1 | 1 | ----cargo From 1b25322d8e729694521ef0f0be1707d0c473d5f9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 15 Sep 2025 21:07:12 -0500 Subject: [PATCH 3/9] fix(frontmatter): Be more direct in trailing close character error For comparison, the rustc error is: > error: extra characters after frontmatter close are not allowed > --> $DIR/extra-after-end.rs:2:1 > | > LL | ---cargo > | ^^^^^^^^ --- src/cargo/util/frontmatter.rs | 6 +++--- .../testsuite/script/rustc_fixtures/extra-after-end.stderr | 2 +- tests/testsuite/script/rustc_fixtures/mismatch-1.stderr | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cargo/util/frontmatter.rs b/src/cargo/util/frontmatter.rs index f40c36403fc..ab77e4a9298 100644 --- a/src/cargo/util/frontmatter.rs +++ b/src/cargo/util/frontmatter.rs @@ -132,7 +132,7 @@ impl<'s> ScriptSource<'s> { if !after_closing_fence.is_empty() { // extra characters beyond the original fence pattern, even if they are extra `-` return Err(FrontmatterError::new( - format!("trailing characters found after frontmatter close"), + format!("unexpected characters after frontmatter close"), close_end..content_start, )); } @@ -584,7 +584,7 @@ content: "\nfn main() {}\n" fn main() {} "#, ), - str!["trailing characters found after frontmatter close"], + str!["unexpected characters after frontmatter close"], ); } @@ -621,7 +621,7 @@ time="0.1.25" fn main() {} "#, ), - str!["trailing characters found after frontmatter close"], + str!["unexpected characters after frontmatter close"], ); } diff --git a/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr b/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr index 15d0acb9a55..5d962ba452d 100644 --- a/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr +++ b/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr @@ -1,4 +1,4 @@ -[ERROR] trailing characters found after frontmatter close +[ERROR] unexpected characters after frontmatter close --> script:2:4 | 2 | ---cargo diff --git a/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr b/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr index ed7cc029029..c77694329f5 100644 --- a/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr +++ b/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr @@ -1,4 +1,4 @@ -[ERROR] trailing characters found after frontmatter close +[ERROR] unexpected characters after frontmatter close --> script:3:4 | 3 | ---- From 0cbd02f0932d52852d522ed5fd55c3b43fc84031 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 16 Sep 2025 10:55:29 -0500 Subject: [PATCH 4/9] fix(fontmatter): Specialize the extra frontmatter close for dashes For comparison, rustc's message is: > error: frontmatter close does not match the opening > --> $DIR/mismatch-1.rs:1:1 > | > LL | ---cargo > | ^-- > | | > | _the opening here has 3 dashes... > | | > LL | | > LL | | ---- > | |_---^ > | | > | ...while the close has 4 dashes --- src/cargo/util/frontmatter.rs | 27 ++++++++++++++----- .../script/rustc_fixtures/mismatch-1.stderr | 8 +++--- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/cargo/util/frontmatter.rs b/src/cargo/util/frontmatter.rs index ab77e4a9298..50b5b584667 100644 --- a/src/cargo/util/frontmatter.rs +++ b/src/cargo/util/frontmatter.rs @@ -128,13 +128,26 @@ impl<'s> ScriptSource<'s> { .unwrap_or_else(|| input.eof_offset()), ); let content_start = input.current_token_start(); - let after_closing_fence = after_closing_fence.trim_matches(is_whitespace); - if !after_closing_fence.is_empty() { - // extra characters beyond the original fence pattern, even if they are extra `-` + let extra_dashes = after_closing_fence + .chars() + .take_while(|b| *b == FENCE_CHAR) + .count(); + if 0 < extra_dashes { + let extra_start = close_end; + let extra_end = extra_start + extra_dashes; return Err(FrontmatterError::new( - format!("unexpected characters after frontmatter close"), - close_end..content_start, + format!("closing code fence has {extra_dashes} more `-` than the opening fence"), + extra_start..extra_end, )); + } else { + let after_closing_fence = after_closing_fence.trim_matches(is_whitespace); + if !after_closing_fence.is_empty() { + // extra characters beyond the original fence pattern + return Err(FrontmatterError::new( + format!("unexpected characters after frontmatter close"), + close_end..content_start, + )); + } } source.content = content_start..content_end; @@ -584,7 +597,7 @@ content: "\nfn main() {}\n" fn main() {} "#, ), - str!["unexpected characters after frontmatter close"], + str!["closing code fence has 2 more `-` than the opening fence"], ); } @@ -621,7 +634,7 @@ time="0.1.25" fn main() {} "#, ), - str!["unexpected characters after frontmatter close"], + str!["closing code fence has 1 more `-` than the opening fence"], ); } diff --git a/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr b/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr index c77694329f5..8b47df61f87 100644 --- a/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr +++ b/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr @@ -1,7 +1,5 @@ -[ERROR] unexpected characters after frontmatter close +[ERROR] closing code fence has 1 more `-` than the opening fence --> script:3:4 | -3 | ---- - | ____^ -4 | | - | |_^ +3 | ---- + | ^ From b6326d3255283f0fe353b8c85446e371634f5c36 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 16 Sep 2025 11:10:32 -0500 Subject: [PATCH 5/9] fix(frontmatter): Have too few error match too many on close --- src/cargo/util/frontmatter.rs | 5 ++++- tests/testsuite/script/rustc_fixtures/mismatch-2.stderr | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cargo/util/frontmatter.rs b/src/cargo/util/frontmatter.rs index 50b5b584667..c3a7a33d23c 100644 --- a/src/cargo/util/frontmatter.rs +++ b/src/cargo/util/frontmatter.rs @@ -103,8 +103,11 @@ impl<'s> ScriptSource<'s> { let close_start = input.current_token_start(); let _ = input.next_slice(len); let close_end = input.current_token_start(); + let fewer_dashes = fence_length - len; return Err(FrontmatterError::new( - format!("closing code fence has too few `-`"), + format!( + "closing code fence has {fewer_dashes} less `-` than the opening fence" + ), close_start..close_end, )); } diff --git a/tests/testsuite/script/rustc_fixtures/mismatch-2.stderr b/tests/testsuite/script/rustc_fixtures/mismatch-2.stderr index 48667df6a70..65464143319 100644 --- a/tests/testsuite/script/rustc_fixtures/mismatch-2.stderr +++ b/tests/testsuite/script/rustc_fixtures/mismatch-2.stderr @@ -1,4 +1,4 @@ -[ERROR] closing code fence has too few `-` +[ERROR] closing code fence has 1 less `-` than the opening fence --> script:3:1 | 3 | ---cargo From 42027af7060ec6bb82cec21f7d68ba3987895714 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 16 Sep 2025 11:12:54 -0500 Subject: [PATCH 6/9] refactor(frontmatter): Clarify the span --- src/cargo/util/frontmatter.rs | 8 ++++---- src/cargo/util/toml/mod.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cargo/util/frontmatter.rs b/src/cargo/util/frontmatter.rs index c3a7a33d23c..8192beb9e01 100644 --- a/src/cargo/util/frontmatter.rs +++ b/src/cargo/util/frontmatter.rs @@ -286,14 +286,14 @@ fn is_whitespace(c: char) -> bool { #[derive(Debug)] pub struct FrontmatterError { message: String, - span: Span, + primary_span: Span, } impl FrontmatterError { pub fn new(message: impl Into, span: Span) -> Self { Self { message: message.into(), - span, + primary_span: span, } } @@ -301,8 +301,8 @@ impl FrontmatterError { self.message.as_str() } - pub fn span(&self) -> Span { - self.span.clone() + pub fn primary_span(&self) -> Span { + self.primary_span.clone() } } diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 885e49c19d7..6899d3ef0d5 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -2783,7 +2783,7 @@ fn emit_frontmatter_diagnostic( manifest_file: &Path, gctx: &GlobalContext, ) -> anyhow::Error { - let span = e.span(); + let primary_span = e.primary_span(); // Get the path to the manifest, relative to the cwd let manifest_path = diff_paths(manifest_file, gctx.cwd()) @@ -2793,7 +2793,7 @@ fn emit_frontmatter_diagnostic( let group = Group::with_title(Level::ERROR.primary_title(e.message())).element( Snippet::source(contents) .path(manifest_path) - .annotation(AnnotationKind::Primary.span(span)), + .annotation(AnnotationKind::Primary.span(primary_span)), ); if let Err(err) = gctx.shell().print_report(&[group], true) { From 640e12cdfc2fc4885bc55e1fd5c5365a99d60b2e Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 16 Sep 2025 11:29:53 -0500 Subject: [PATCH 7/9] refactor(frontmatter): Pull out common span --- src/cargo/util/toml/embedded.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index b29bf45691d..5bf52de436d 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -10,17 +10,18 @@ pub(super) fn expand_manifest(content: &str) -> Result match source.info() { Some("cargo") | None => {} Some(other) => { + let info_span = source.info_span().unwrap(); if let Some(remainder) = other.strip_prefix("cargo,") { return Err(FrontmatterError::new( format!("unsupported frontmatter infostring attributes: `{remainder}`"), - source.info_span().unwrap(), + info_span, )); } else { return Err(FrontmatterError::new( format!( "unsupported frontmatter infostring `{other}`; specify `cargo` for embedding a manifest" ), - source.info_span().unwrap(), + info_span, )); } } From 5b4d0cfad8e753b8953c4e51a9ae2a75d3ad901c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 16 Sep 2025 11:38:03 -0500 Subject: [PATCH 8/9] fix(frontmatter): Add more visible context to errors --- src/cargo/util/frontmatter.rs | 38 ++++++++++++++----- src/cargo/util/toml/embedded.rs | 6 ++- src/cargo/util/toml/mod.rs | 7 +++- .../dot-in-infostring-leading.stderr | 3 ++ .../dot-in-infostring-non-leading.stderr | 2 + .../rustc_fixtures/extra-after-end.stderr | 1 + .../frontmatter-whitespace-2.stderr | 10 +++-- .../hyphen-in-infostring-leading.stderr | 3 ++ .../hyphen-in-infostring-non-leading.stderr | 2 + .../rustc_fixtures/infostring-fail.stderr | 3 ++ .../script/rustc_fixtures/mismatch-1.stderr | 2 + .../script/rustc_fixtures/mismatch-2.stderr | 2 + .../rustc_fixtures/multifrontmatter.stderr | 3 ++ .../script/rustc_fixtures/unclosed-1.stderr | 10 +++-- .../script/rustc_fixtures/unclosed-2.stderr | 10 +++-- .../script/rustc_fixtures/unclosed-3.stderr | 10 +++-- .../script/rustc_fixtures/unclosed-4.stderr | 10 +++-- .../script/rustc_fixtures/unclosed-5.stderr | 10 +++-- 18 files changed, 95 insertions(+), 37 deletions(-) diff --git a/src/cargo/util/frontmatter.rs b/src/cargo/util/frontmatter.rs index 8192beb9e01..dadf36d4b04 100644 --- a/src/cargo/util/frontmatter.rs +++ b/src/cargo/util/frontmatter.rs @@ -72,8 +72,8 @@ impl<'s> ScriptSource<'s> { format!( "found {fence_length} `{FENCE_CHAR}` in rust frontmatter, expected at least 3" ), - open_start..open_end, - )); + raw.len()..raw.len(), + ).push_visible_span(open_start..open_end)); } _ => {} } @@ -81,8 +81,9 @@ impl<'s> ScriptSource<'s> { let Some(info_nl) = input.find_slice("\n") else { return Err(FrontmatterError::new( format!("unclosed frontmatter; expected `{fence_pattern}`"), - open_start..open_end, - )); + raw.len()..raw.len(), + ) + .push_visible_span(open_start..open_end)); }; let info = input.next_slice(info_nl.start); let info = info.trim_matches(is_whitespace); @@ -109,12 +110,14 @@ impl<'s> ScriptSource<'s> { "closing code fence has {fewer_dashes} less `-` than the opening fence" ), close_start..close_end, - )); + ) + .push_visible_span(open_start..open_end)); } return Err(FrontmatterError::new( format!("unclosed frontmatter; expected `{fence_pattern}`"), - open_start..open_end, - )); + raw.len()..raw.len(), + ) + .push_visible_span(open_start..open_end)); }; let frontmatter_start = input.current_token_start() + 1; // skip nl from infostring let _ = input.next_slice(frontmatter_nl.start + 1); @@ -141,7 +144,8 @@ impl<'s> ScriptSource<'s> { return Err(FrontmatterError::new( format!("closing code fence has {extra_dashes} more `-` than the opening fence"), extra_start..extra_end, - )); + ) + .push_visible_span(open_start..open_end)); } else { let after_closing_fence = after_closing_fence.trim_matches(is_whitespace); if !after_closing_fence.is_empty() { @@ -149,7 +153,8 @@ impl<'s> ScriptSource<'s> { return Err(FrontmatterError::new( format!("unexpected characters after frontmatter close"), close_end..content_start, - )); + ) + .push_visible_span(open_start..open_end)); } } @@ -169,7 +174,9 @@ impl<'s> ScriptSource<'s> { return Err(FrontmatterError::new( format!("only one frontmatter is supported"), fence_start..fence_end, - )); + ) + .push_visible_span(open_start..open_end) + .push_visible_span(close_start..close_end)); } Ok(source) @@ -287,6 +294,7 @@ fn is_whitespace(c: char) -> bool { pub struct FrontmatterError { message: String, primary_span: Span, + visible_spans: Vec, } impl FrontmatterError { @@ -294,9 +302,15 @@ impl FrontmatterError { Self { message: message.into(), primary_span: span, + visible_spans: Vec::new(), } } + pub fn push_visible_span(mut self, span: Span) -> Self { + self.visible_spans.push(span); + self + } + pub fn message(&self) -> &str { self.message.as_str() } @@ -304,6 +318,10 @@ impl FrontmatterError { pub fn primary_span(&self) -> Span { self.primary_span.clone() } + + pub fn visible_spans(&self) -> &[Span] { + &self.visible_spans + } } impl std::fmt::Display for FrontmatterError { diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 5bf52de436d..189257e8a64 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -11,18 +11,20 @@ pub(super) fn expand_manifest(content: &str) -> Result Some("cargo") | None => {} Some(other) => { let info_span = source.info_span().unwrap(); + let close_span = source.close_span().unwrap(); if let Some(remainder) = other.strip_prefix("cargo,") { return Err(FrontmatterError::new( format!("unsupported frontmatter infostring attributes: `{remainder}`"), info_span, - )); + ) + .push_visible_span(close_span)); } else { return Err(FrontmatterError::new( format!( "unsupported frontmatter infostring `{other}`; specify `cargo` for embedding a manifest" ), info_span, - )); + ).push_visible_span(close_span)); } } } diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 6899d3ef0d5..d0a7ced2eef 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -2793,7 +2793,12 @@ fn emit_frontmatter_diagnostic( let group = Group::with_title(Level::ERROR.primary_title(e.message())).element( Snippet::source(contents) .path(manifest_path) - .annotation(AnnotationKind::Primary.span(primary_span)), + .annotation(AnnotationKind::Primary.span(primary_span)) + .annotations( + e.visible_spans() + .iter() + .map(|s| AnnotationKind::Visible.span(s.clone())), + ), ); if let Err(err) = gctx.shell().print_report(&[group], true) { diff --git a/tests/testsuite/script/rustc_fixtures/dot-in-infostring-leading.stderr b/tests/testsuite/script/rustc_fixtures/dot-in-infostring-leading.stderr index 01521f3efa1..bd16d910d1c 100644 --- a/tests/testsuite/script/rustc_fixtures/dot-in-infostring-leading.stderr +++ b/tests/testsuite/script/rustc_fixtures/dot-in-infostring-leading.stderr @@ -3,3 +3,6 @@ | 1 | ---.toml | ^^^^^ +2 | //~^ ERROR: invalid infostring for frontmatter +3 | --- + | diff --git a/tests/testsuite/script/rustc_fixtures/dot-in-infostring-non-leading.stderr b/tests/testsuite/script/rustc_fixtures/dot-in-infostring-non-leading.stderr index d075c8bb3d1..e249336a684 100644 --- a/tests/testsuite/script/rustc_fixtures/dot-in-infostring-non-leading.stderr +++ b/tests/testsuite/script/rustc_fixtures/dot-in-infostring-non-leading.stderr @@ -3,3 +3,5 @@ | 1 | ---Cargo.toml | ^^^^^^^^^^ +2 | --- + | diff --git a/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr b/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr index 5d962ba452d..15db7e59c43 100644 --- a/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr +++ b/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr @@ -1,6 +1,7 @@ [ERROR] unexpected characters after frontmatter close --> script:2:4 | +1 | --- 2 | ---cargo | ____^ 3 | | //~^ ERROR: extra characters after frontmatter close are not allowed diff --git a/tests/testsuite/script/rustc_fixtures/frontmatter-whitespace-2.stderr b/tests/testsuite/script/rustc_fixtures/frontmatter-whitespace-2.stderr index d567931e305..632b16a2ec7 100644 --- a/tests/testsuite/script/rustc_fixtures/frontmatter-whitespace-2.stderr +++ b/tests/testsuite/script/rustc_fixtures/frontmatter-whitespace-2.stderr @@ -1,5 +1,7 @@ [ERROR] unclosed frontmatter; expected `---` - --> script:1:1 - | -1 | ---cargo - | ^^^ + --> script:14:56 + | + 1 | ---cargo +... +14 | // within them and get treated as a frontmatter close. + | ^ diff --git a/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-leading.stderr b/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-leading.stderr index ab78161fd0c..76dbe84e7af 100644 --- a/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-leading.stderr +++ b/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-leading.stderr @@ -3,3 +3,6 @@ | 1 | --- -toml | ^^^^^ +2 | //~^ ERROR: invalid infostring for frontmatter +3 | --- + | diff --git a/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-non-leading.stderr b/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-non-leading.stderr index 76838fec241..e58bccb38ae 100644 --- a/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-non-leading.stderr +++ b/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-non-leading.stderr @@ -3,3 +3,5 @@ | 1 | --- Cargo-toml | ^^^^^^^^^^ +2 | --- + | diff --git a/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr b/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr index 5bc6438f882..1c50689821c 100644 --- a/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr +++ b/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr @@ -3,3 +3,6 @@ | 1 | ---cargo,clippy | ^^^^^^^^^^^^ +2 | //~^ ERROR: invalid infostring for frontmatter +3 | --- + | diff --git a/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr b/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr index 8b47df61f87..f388d8922d9 100644 --- a/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr +++ b/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr @@ -1,5 +1,7 @@ [ERROR] closing code fence has 1 more `-` than the opening fence --> script:3:4 | +1 | ---cargo +2 | //~^ ERROR: frontmatter close does not match the opening 3 | ---- | ^ diff --git a/tests/testsuite/script/rustc_fixtures/mismatch-2.stderr b/tests/testsuite/script/rustc_fixtures/mismatch-2.stderr index 65464143319..01b8916a918 100644 --- a/tests/testsuite/script/rustc_fixtures/mismatch-2.stderr +++ b/tests/testsuite/script/rustc_fixtures/mismatch-2.stderr @@ -1,5 +1,7 @@ [ERROR] closing code fence has 1 less `-` than the opening fence --> script:3:1 | +1 | ----cargo +2 | //~^ ERROR: frontmatter close does not match the opening 3 | ---cargo | ^^^ diff --git a/tests/testsuite/script/rustc_fixtures/multifrontmatter.stderr b/tests/testsuite/script/rustc_fixtures/multifrontmatter.stderr index e073928c5d9..733e4b8c684 100644 --- a/tests/testsuite/script/rustc_fixtures/multifrontmatter.stderr +++ b/tests/testsuite/script/rustc_fixtures/multifrontmatter.stderr @@ -1,5 +1,8 @@ [ERROR] only one frontmatter is supported --> script:4:1 | +1 | --- +2 | --- +3 | 4 | --- | ^^^ diff --git a/tests/testsuite/script/rustc_fixtures/unclosed-1.stderr b/tests/testsuite/script/rustc_fixtures/unclosed-1.stderr index 89d0b151d5d..74539be8f63 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-1.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-1.stderr @@ -1,5 +1,7 @@ [ERROR] unclosed frontmatter; expected `----` - --> script:1:1 - | -1 | ----cargo - | ^^^^ + --> script:10:14 + | + 1 | ----cargo +... +10 | fn main() {} + | ^ diff --git a/tests/testsuite/script/rustc_fixtures/unclosed-2.stderr b/tests/testsuite/script/rustc_fixtures/unclosed-2.stderr index 89d0b151d5d..8e77c6bab03 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-2.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-2.stderr @@ -1,5 +1,7 @@ [ERROR] unclosed frontmatter; expected `----` - --> script:1:1 - | -1 | ----cargo - | ^^^^ + --> script:15:3 + | + 1 | ----cargo +... +15 | } + | ^ diff --git a/tests/testsuite/script/rustc_fixtures/unclosed-3.stderr b/tests/testsuite/script/rustc_fixtures/unclosed-3.stderr index 89d0b151d5d..b0e472822f0 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-3.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-3.stderr @@ -1,5 +1,7 @@ [ERROR] unclosed frontmatter; expected `----` - --> script:1:1 - | -1 | ----cargo - | ^^^^ + --> script:16:47 + | + 1 | ----cargo +... +16 | //~^ ERROR: unexpected closing delimiter: `}` + | ^ diff --git a/tests/testsuite/script/rustc_fixtures/unclosed-4.stderr b/tests/testsuite/script/rustc_fixtures/unclosed-4.stderr index 89d0b151d5d..b253a75ef43 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-4.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-4.stderr @@ -1,5 +1,7 @@ [ERROR] unclosed frontmatter; expected `----` - --> script:1:1 - | -1 | ----cargo - | ^^^^ + --> script:9:14 + | + 1 | ----cargo +... + 9 | fn main() {} + | ^ diff --git a/tests/testsuite/script/rustc_fixtures/unclosed-5.stderr b/tests/testsuite/script/rustc_fixtures/unclosed-5.stderr index 89d0b151d5d..74539be8f63 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-5.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-5.stderr @@ -1,5 +1,7 @@ [ERROR] unclosed frontmatter; expected `----` - --> script:1:1 - | -1 | ----cargo - | ^^^^ + --> script:10:14 + | + 1 | ----cargo +... +10 | fn main() {} + | ^ From b674a603849c80fae33a29d2e975eecafa67a503 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 16 Sep 2025 11:43:20 -0500 Subject: [PATCH 9/9] fix(frontmatter): Be more specific about trailing character span --- src/cargo/util/frontmatter.rs | 4 +++- .../script/rustc_fixtures/extra-after-end.stderr | 8 +++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cargo/util/frontmatter.rs b/src/cargo/util/frontmatter.rs index dadf36d4b04..7eeb6ce4814 100644 --- a/src/cargo/util/frontmatter.rs +++ b/src/cargo/util/frontmatter.rs @@ -150,9 +150,11 @@ impl<'s> ScriptSource<'s> { let after_closing_fence = after_closing_fence.trim_matches(is_whitespace); if !after_closing_fence.is_empty() { // extra characters beyond the original fence pattern + let after_start = after_closing_fence.offset_from(&raw); + let after_end = after_start + after_closing_fence.len(); return Err(FrontmatterError::new( format!("unexpected characters after frontmatter close"), - close_end..content_start, + after_start..after_end, ) .push_visible_span(open_start..open_end)); } diff --git a/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr b/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr index 15db7e59c43..dc353a2e8cc 100644 --- a/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr +++ b/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr @@ -1,8 +1,6 @@ [ERROR] unexpected characters after frontmatter close --> script:2:4 | -1 | --- -2 | ---cargo - | ____^ -3 | | //~^ ERROR: extra characters after frontmatter close are not allowed - | |_^ +1 | --- +2 | ---cargo + | ^^^^^