diff --git a/src/cargo/util/frontmatter.rs b/src/cargo/util/frontmatter.rs index 009fd3726..7eeb6ce48 100644 --- a/src/cargo/util/frontmatter.rs +++ b/src/cargo/util/frontmatter.rs @@ -72,17 +72,18 @@ 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)); } _ => {} } 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"), - open_start..open_end, - )); + format!("unclosed frontmatter; expected `{fence_pattern}`"), + 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); @@ -103,15 +104,20 @@ 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, - )); + ) + .push_visible_span(open_start..open_end)); } return Err(FrontmatterError::new( - format!("no closing `{fence_pattern}` found for frontmatter"), - open_start..open_end, - )); + format!("unclosed frontmatter; expected `{fence_pattern}`"), + 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); @@ -128,13 +134,30 @@ 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!("trailing characters found after frontmatter close"), - close_end..content_start, - )); + 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() { + // 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"), + after_start..after_end, + ) + .push_visible_span(open_start..open_end)); + } } source.content = content_start..content_end; @@ -153,7 +176,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) @@ -270,23 +295,34 @@ fn is_whitespace(c: char) -> bool { #[derive(Debug)] pub struct FrontmatterError { message: String, - span: Span, + primary_span: Span, + visible_spans: Vec, } impl FrontmatterError { pub fn new(message: impl Into, span: Span) -> Self { Self { message: message.into(), - span, + 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() } - pub fn span(&self) -> Span { - self.span.clone() + pub fn primary_span(&self) -> Span { + self.primary_span.clone() + } + + pub fn visible_spans(&self) -> &[Span] { + &self.visible_spans } } @@ -584,7 +620,7 @@ content: "\nfn main() {}\n" fn main() {} "#, ), - str!["trailing characters found after frontmatter close"], + str!["closing code fence has 2 more `-` than the opening fence"], ); } @@ -621,7 +657,7 @@ time="0.1.25" fn main() {} "#, ), - str!["trailing characters found after frontmatter close"], + str!["closing code fence has 1 more `-` than the opening fence"], ); } @@ -636,7 +672,7 @@ time="0.1.25" fn main() {} "#, ), - str!["no closing `---` found for frontmatter"], + str!["unclosed frontmatter; expected `---`"], ); } } diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index dd10ebc3e..189257e8a 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -10,20 +10,21 @@ pub(super) fn expand_manifest(content: &str) -> Result match source.info() { 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!( - "cargo does not support frontmatter infostring attributes like `{remainder}` at this time" - ), - source.info_span().unwrap(), - )); + format!("unsupported frontmatter infostring attributes: `{remainder}`"), + info_span, + ) + .push_visible_span(close_span)); } 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(), - )); + info_span, + ).push_visible_span(close_span)); } } } diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 885e49c19..d0a7ced2e 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,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(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 cccb08026..bd16d910d 100644 --- a/tests/testsuite/script/rustc_fixtures/dot-in-infostring-leading.stderr +++ b/tests/testsuite/script/rustc_fixtures/dot-in-infostring-leading.stderr @@ -1,5 +1,8 @@ -[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 | ^^^^^ +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 ac684b2ea..e249336a6 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,5 +1,7 @@ -[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 | ^^^^^^^^^^ +2 | --- + | diff --git a/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr b/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr index 15d0acb9a..dc353a2e8 100644 --- a/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr +++ b/tests/testsuite/script/rustc_fixtures/extra-after-end.stderr @@ -1,7 +1,6 @@ -[ERROR] trailing characters found after frontmatter close +[ERROR] unexpected characters after frontmatter close --> script:2:4 | -2 | ---cargo - | ____^ -3 | | //~^ ERROR: extra characters after frontmatter close are not allowed - | |_^ +1 | --- +2 | ---cargo + | ^^^^^ diff --git a/tests/testsuite/script/rustc_fixtures/frontmatter-whitespace-2.stderr b/tests/testsuite/script/rustc_fixtures/frontmatter-whitespace-2.stderr index f95dd5274..632b16a2e 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] no closing `---` found for frontmatter - --> script:1:1 - | -1 | ---cargo - | ^^^ +[ERROR] unclosed frontmatter; expected `---` + --> 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 2b39032a9..76dbe84e7 100644 --- a/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-leading.stderr +++ b/tests/testsuite/script/rustc_fixtures/hyphen-in-infostring-leading.stderr @@ -1,5 +1,8 @@ -[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 | ^^^^^ +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 a19c79b7d..e58bccb38 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,5 +1,7 @@ -[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 | ^^^^^^^^^^ +2 | --- + | diff --git a/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr b/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr index bbd2e32f2..1c5068982 100644 --- a/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr +++ b/tests/testsuite/script/rustc_fixtures/infostring-fail.stderr @@ -1,5 +1,8 @@ -[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 | ^^^^^^^^^^^^ +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 ed7cc0290..f388d8922 100644 --- a/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr +++ b/tests/testsuite/script/rustc_fixtures/mismatch-1.stderr @@ -1,7 +1,7 @@ -[ERROR] trailing characters found after frontmatter close +[ERROR] closing code fence has 1 more `-` than the opening fence --> script:3:4 | -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 48667df6a..01b8916a9 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 too few `-` +[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 e073928c5..733e4b8c6 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 31743531c..74539be8f 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-1.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-1.stderr @@ -1,5 +1,7 @@ -[ERROR] no closing `----` found for frontmatter - --> script:1:1 - | -1 | ----cargo - | ^^^^ +[ERROR] unclosed frontmatter; expected `----` + --> 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 31743531c..8e77c6bab 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-2.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-2.stderr @@ -1,5 +1,7 @@ -[ERROR] no closing `----` found for frontmatter - --> script:1:1 - | -1 | ----cargo - | ^^^^ +[ERROR] unclosed frontmatter; expected `----` + --> 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 31743531c..b0e472822 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-3.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-3.stderr @@ -1,5 +1,7 @@ -[ERROR] no closing `----` found for frontmatter - --> script:1:1 - | -1 | ----cargo - | ^^^^ +[ERROR] unclosed frontmatter; expected `----` + --> 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 31743531c..b253a75ef 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-4.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-4.stderr @@ -1,5 +1,7 @@ -[ERROR] no closing `----` found for frontmatter - --> script:1:1 - | -1 | ----cargo - | ^^^^ +[ERROR] unclosed frontmatter; expected `----` + --> 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 31743531c..74539be8f 100644 --- a/tests/testsuite/script/rustc_fixtures/unclosed-5.stderr +++ b/tests/testsuite/script/rustc_fixtures/unclosed-5.stderr @@ -1,5 +1,7 @@ -[ERROR] no closing `----` found for frontmatter - --> script:1:1 - | -1 | ----cargo - | ^^^^ +[ERROR] unclosed frontmatter; expected `----` + --> script:10:14 + | + 1 | ----cargo +... +10 | fn main() {} + | ^