diff --git a/Cargo.lock b/Cargo.lock index d0508017473..ccbdde82f86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,11 +40,12 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "annotate-snippets" -version = "0.11.5" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" +checksum = "7a851f39ec7e23bf1c30faf3844a496fee4db701f5f840f68d1f78f00e697892" dependencies = [ "anstyle", + "memchr", "unicode-width", ] diff --git a/Cargo.toml b/Cargo.toml index 30dfc609533..7fd04749b03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ homepage = "https://github.com/rust-lang/cargo" repository = "https://github.com/rust-lang/cargo" [workspace.dependencies] -annotate-snippets = "0.11.5" +annotate-snippets = { version = "0.12.1", features = ["simd"] } anstream = "0.6.20" anstyle = "1.0.11" anyhow = "1.0.98" diff --git a/src/cargo/core/shell.rs b/src/cargo/core/shell.rs index 53b18964564..918b59fbc2e 100644 --- a/src/cargo/core/shell.rs +++ b/src/cargo/core/shell.rs @@ -2,7 +2,7 @@ use std::fmt; use std::io::IsTerminal; use std::io::prelude::*; -use annotate_snippets::{Message, Renderer}; +use annotate_snippets::{Renderer, Report}; use anstream::AutoStream; use anstyle::Style; @@ -406,8 +406,8 @@ impl Shell { Ok(()) } - /// Prints the passed in [Message] to stderr - pub fn print_message(&mut self, message: Message<'_>) -> std::io::Result<()> { + /// Prints the passed in [`Report`] to stderr + pub fn print_report(&mut self, report: Report<'_>) -> std::io::Result<()> { let term_width = self .err_width() .diagnostic_terminal_width() @@ -415,7 +415,7 @@ impl Shell { writeln!( self.err(), "{}", - Renderer::styled().term_width(term_width).render(message) + Renderer::styled().term_width(term_width).render(report) ) } } diff --git a/src/cargo/util/lints.rs b/src/cargo/util/lints.rs index 30e79848bab..06ea6f47084 100644 --- a/src/cargo/util/lints.rs +++ b/src/cargo/util/lints.rs @@ -1,6 +1,6 @@ use crate::core::{Edition, Feature, Features, Manifest, Package}; use crate::{CargoResult, GlobalContext}; -use annotate_snippets::{Level, Snippet}; +use annotate_snippets::{AnnotationKind, Group, Level, Snippet}; use cargo_util_schemas::manifest::{TomlLintLevel, TomlToolLints}; use pathdiff::diff_paths; use std::fmt::Display; @@ -133,78 +133,69 @@ fn verify_feature_enabled( dash_feature_name ); - let message = if let Some(span) = - get_span(manifest.document(), &["lints", "cargo", lint_name], false) + let (contents, path, span) = if let Some(span) = + get_key_value_span(manifest.document(), &["lints", "cargo", lint_name]) { - Level::Error - .title(&title) - .snippet( - Snippet::source(manifest.contents()) - .origin(&manifest_path) - .annotation(Level::Error.span(span).label(&label)) - .fold(true), - ) - .footer(Level::Help.title(&help)) + (manifest.contents(), manifest_path, span) + } else if let Some(lint_span) = + get_key_value_span(ws_document, &["workspace", "lints", "cargo", lint_name]) + { + (ws_contents, ws_path, lint_span) } else { - let lint_span = get_span( - ws_document, - &["workspace", "lints", "cargo", lint_name], - false, - ) - .unwrap_or_else(|| { - panic!("could not find `cargo::{lint_name}` in `[lints]`, or `[workspace.lints]` ") - }); + panic!("could not find `cargo::{lint_name}` in `[lints]`, or `[workspace.lints]` ") + }; - let inherited_note = if let (Some(inherit_span_key), Some(inherit_span_value)) = ( - get_span(manifest.document(), &["lints", "workspace"], false), - get_span(manifest.document(), &["lints", "workspace"], true), - ) { - Level::Note.title(&second_title).snippet( + let mut report = Vec::new(); + report.push( + Group::with_title(Level::ERROR.primary_title(title)) + .element( + Snippet::source(contents) + .path(path) + .annotation(AnnotationKind::Primary.span(span.key).label(label)), + ) + .element(Level::HELP.message(help)), + ); + + if let Some(inherit_span) = get_key_value_span(manifest.document(), &["lints", "workspace"]) + { + report.push( + Group::with_title(Level::NOTE.secondary_title(second_title)).element( Snippet::source(manifest.contents()) - .origin(&manifest_path) + .path(manifest_path) .annotation( - Level::Note.span(inherit_span_key.start..inherit_span_value.end), - ) - .fold(true), - ) - } else { - Level::Note.title(&second_title) - }; - - Level::Error - .title(&title) - .snippet( - Snippet::source(ws_contents) - .origin(&ws_path) - .annotation(Level::Error.span(lint_span).label(&label)) - .fold(true), - ) - .footer(inherited_note) - .footer(Level::Help.title(&help)) - }; + AnnotationKind::Context + .span(inherit_span.key.start..inherit_span.value.end), + ), + ), + ); + } *error_count += 1; - gctx.shell().print_message(message)?; + gctx.shell().print_report(&report)?; } Ok(()) } -pub fn get_span( +#[derive(Clone)] +pub struct TomlSpan { + pub key: Range, + pub value: Range, +} + +pub fn get_key_value_span( document: &toml::Spanned>, path: &[&str], - get_value: bool, -) -> Option> { +) -> Option { let mut table = document.get_ref(); let mut iter = path.into_iter().peekable(); while let Some(key) = iter.next() { let key_s: &str = key.as_ref(); let (key, item) = table.get_key_value(key_s)?; if iter.peek().is_none() { - return if get_value { - Some(item.span()) - } else { - Some(key.span()) - }; + return Some(TomlSpan { + key: key.span(), + value: item.span(), + }); } if let Some(next_table) = item.get_ref().as_table() { table = next_table; @@ -213,7 +204,10 @@ pub fn get_span( if let Some(array) = item.get_ref().as_array() { let next = iter.next().unwrap(); return array.iter().find_map(|item| match item.get_ref() { - toml::de::DeValue::String(s) if s == next => Some(item.span()), + toml::de::DeValue::String(s) if s == next => Some(TomlSpan { + key: key.span(), + value: item.span(), + }), _ => None, }); } @@ -337,12 +331,12 @@ impl LintLevel { self == &LintLevel::Forbid || self == &LintLevel::Deny } - pub fn to_diagnostic_level(self) -> Level { + pub fn to_diagnostic_level(self) -> Level<'static> { match self { LintLevel::Allow => unreachable!("allow does not map to a diagnostic level"), - LintLevel::Warn => Level::Warning, - LintLevel::Deny => Level::Error, - LintLevel::Forbid => Level::Error, + LintLevel::Warn => Level::WARNING, + LintLevel::Deny => Level::ERROR, + LintLevel::Forbid => Level::ERROR, } } } @@ -455,19 +449,17 @@ pub fn check_im_a_teapot( let manifest_path = rel_cwd_manifest_path(path, gctx); let emitted_reason = IM_A_TEAPOT.emitted_source(lint_level, reason); - let key_span = get_span(manifest.document(), &["package", "im-a-teapot"], false).unwrap(); - let value_span = get_span(manifest.document(), &["package", "im-a-teapot"], true).unwrap(); - let message = level - .title(IM_A_TEAPOT.desc) - .snippet( + let span = get_key_value_span(manifest.document(), &["package", "im-a-teapot"]).unwrap(); + + let report = &[Group::with_title(level.primary_title(IM_A_TEAPOT.desc)) + .element( Snippet::source(manifest.contents()) - .origin(&manifest_path) - .annotation(level.span(key_span.start..value_span.end)) - .fold(true), + .path(&manifest_path) + .annotation(AnnotationKind::Primary.span(span.key.start..span.value.end)), ) - .footer(Level::Note.title(&emitted_reason)); + .element(Level::NOTE.message(&emitted_reason))]; - gctx.shell().print_message(message)?; + gctx.shell().print_report(report)?; } Ok(()) } @@ -535,64 +527,48 @@ fn output_unknown_lints( let help = matching.map(|(name, kind)| format!("there is a {kind} with a similar name: `{name}`")); - let mut footers = Vec::new(); + let (contents, path, span) = if let Some(span) = + get_key_value_span(manifest.document(), &["lints", "cargo", lint_name]) + { + (manifest.contents(), manifest_path, span) + } else if let Some(lint_span) = + get_key_value_span(ws_document, &["workspace", "lints", "cargo", lint_name]) + { + (ws_contents, ws_path, lint_span) + } else { + panic!("could not find `cargo::{lint_name}` in `[lints]`, or `[workspace.lints]` ") + }; + + let mut report = Vec::new(); + let mut group = Group::with_title(level.clone().primary_title(title)).element( + Snippet::source(contents) + .path(path) + .annotation(AnnotationKind::Primary.span(span.key)), + ); if emitted_source.is_none() { emitted_source = Some(UNKNOWN_LINTS.emitted_source(lint_level, reason)); - footers.push(Level::Note.title(emitted_source.as_ref().unwrap())); + group = group.element(Level::NOTE.message(emitted_source.as_ref().unwrap())); } + if let Some(help) = help.as_ref() { + group = group.element(Level::HELP.message(help)); + } + report.push(group); - let mut message = if let Some(span) = - get_span(manifest.document(), &["lints", "cargo", lint_name], false) + if let Some(inherit_span) = get_key_value_span(manifest.document(), &["lints", "workspace"]) { - level.title(&title).snippet( - Snippet::source(manifest.contents()) - .origin(&manifest_path) - .annotation(Level::Error.span(span)) - .fold(true), - ) - } else { - let lint_span = get_span( - ws_document, - &["workspace", "lints", "cargo", lint_name], - false, - ) - .unwrap_or_else(|| { - panic!("could not find `cargo::{lint_name}` in `[lints]`, or `[workspace.lints]` ") - }); - - let inherited_note = if let (Some(inherit_span_key), Some(inherit_span_value)) = ( - get_span(manifest.document(), &["lints", "workspace"], false), - get_span(manifest.document(), &["lints", "workspace"], true), - ) { - Level::Note.title(&second_title).snippet( + report.push( + Group::with_title(Level::NOTE.secondary_title(second_title)).element( Snippet::source(manifest.contents()) - .origin(&manifest_path) + .path(manifest_path) .annotation( - Level::Note.span(inherit_span_key.start..inherit_span_value.end), - ) - .fold(true), - ) - } else { - Level::Note.title(&second_title) - }; - footers.push(inherited_note); - - level.title(&title).snippet( - Snippet::source(ws_contents) - .origin(&ws_path) - .annotation(Level::Error.span(lint_span)) - .fold(true), - ) - }; - - if let Some(help) = help.as_ref() { - footers.push(Level::Help.title(help)); - } - for footer in footers { - message = message.footer(footer); + AnnotationKind::Context + .span(inherit_span.key.start..inherit_span.value.end), + ), + ), + ); } - gctx.shell().print_message(message)?; + gctx.shell().print_report(&report)?; } Ok(()) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 82a282153c3..70c7e7b91cf 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1,4 +1,4 @@ -use annotate_snippets::{Level, Snippet}; +use annotate_snippets::{AnnotationKind, Group, Level, Snippet}; use std::borrow::Cow; use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::ffi::OsStr; @@ -32,7 +32,7 @@ use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, Worksp use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY}; use crate::util::errors::{CargoResult, ManifestError}; use crate::util::interning::InternedString; -use crate::util::lints::{get_span, rel_cwd_manifest_path}; +use crate::util::lints::{get_key_value_span, rel_cwd_manifest_path}; use crate::util::{self, GlobalContext, IntoUrl, OptVersionReq, context::ConfigRelativePath}; mod embedded; @@ -1867,8 +1867,8 @@ fn missing_dep_diagnostic( ) -> CargoResult<()> { let dep_name = missing_dep.dep_name; let manifest_path = rel_cwd_manifest_path(manifest_file, gctx); - let feature_value_span = - get_span(&document, &["features", missing_dep.feature.as_str()], true).unwrap(); + let feature_span = + get_key_value_span(&document, &["features", missing_dep.feature.as_str()]).unwrap(); let title = format!( "feature `{}` includes `{}`, but `{}` is not a dependency", @@ -1879,12 +1879,11 @@ fn missing_dep_diagnostic( "`{}` is an unused optional dependency since no feature enables it", &dep_name ); - let message = Level::Error.title(&title); - let snippet = Snippet::source(&contents) - .origin(&manifest_path) - .fold(true) - .annotation(Level::Error.span(feature_value_span.start..feature_value_span.end)); - let message = if missing_dep.weak_optional { + let group = Group::with_title(Level::ERROR.primary_title(&title)); + let snippet = Snippet::source(contents) + .path(manifest_path) + .annotation(AnnotationKind::Primary.span(feature_span.value)); + let group = if missing_dep.weak_optional { let mut orig_deps = vec![ ( orig_toml.dependencies.as_ref(), @@ -1918,19 +1917,22 @@ fn missing_dep_diagnostic( .map(|s| *s) .chain(std::iter::once(dep_name.as_str())) .collect::>(); - let dep_span = get_span(&document, &toml_path, false).unwrap(); + let dep_span = get_key_value_span(&document, &toml_path).unwrap(); - message - .snippet(snippet.annotation(Level::Warning.span(dep_span).label(&info_label))) - .footer(Level::Help.title(&help)) + group + .element( + snippet + .annotation(AnnotationKind::Context.span(dep_span.key).label(info_label)), + ) + .element(Level::HELP.message(help)) } else { - message.snippet(snippet) + group.element(snippet) } } else { - message.snippet(snippet) + group.element(snippet) }; - if let Err(err) = gctx.shell().print_message(message) { + if let Err(err) = gctx.shell().print_report(&[group]) { return Err(err.into()); } Err(AlreadyPrintedError::new(anyhow!("").into()).into()) @@ -2792,13 +2794,13 @@ fn emit_diagnostic( .unwrap_or_else(|| manifest_file.to_path_buf()) .display() .to_string(); - let message = Level::Error.title(e.message()).snippet( + let group = Group::with_title(Level::ERROR.primary_title(e.message())).element( Snippet::source(contents) - .origin(&manifest_path) - .fold(true) - .annotation(Level::Error.span(span)), + .path(manifest_path) + .annotation(AnnotationKind::Primary.span(span)), ); - if let Err(err) = gctx.shell().print_message(message) { + + if let Err(err) = gctx.shell().print_report(&[group]) { return err.into(); } return AlreadyPrintedError::new(e.into()).into(); diff --git a/tests/testsuite/alt_registry.rs b/tests/testsuite/alt_registry.rs index 9099cdd12db..7c49dfa5e07 100644 --- a/tests/testsuite/alt_registry.rs +++ b/tests/testsuite/alt_registry.rs @@ -749,13 +749,12 @@ fn bad_registry_name() { .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid character ` ` in registry name: `bad name`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters) - - + + --> Cargo.toml:8:17 | 8 | [dependencies.bar] | ^^^^^^^^^^^^^^^^^^ - | "#]]) .run(); @@ -1976,13 +1975,12 @@ fn empty_dependency_registry() { .with_status(101) .with_stderr_data(str![[r#" [ERROR] registry name cannot be empty - - + + --> Cargo.toml:8:23 | 8 | bar = { version = "0.1.0", registry = "" } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | "#]]) .run(); diff --git a/tests/testsuite/bad_config.rs b/tests/testsuite/bad_config.rs index 406f0b1b7af..15869bbd4c7 100644 --- a/tests/testsuite/bad_config.rs +++ b/tests/testsuite/bad_config.rs @@ -455,11 +455,12 @@ fn malformed_override() { .with_status(101) .with_stderr_data(str![[r#" [ERROR] newlines are unsupported in inline tables, expected nothing - --> Cargo.toml:9:27 - | -9 | native = { - | ^ - | + --> Cargo.toml:9:27 + | + 9 | native = { + | ___________________________^ +10 | | foo: "bar" + | |_^ "#]]) .run(); @@ -2635,7 +2636,6 @@ fn bad_dependency() { | 9 | bar = 3 | ^ - | "#]]) .run(); @@ -2664,13 +2664,12 @@ fn bad_dependency_true_literal() { .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: boolean `true`, expected a version string like "0.9.8" or a detailed dependency like { version = "0.9.8" } -[NOTE] if you meant to use a workspace member, you can write - dep.workspace = true + [NOTE] if you meant to use a workspace member, you can write + dep.workspace = true --> Cargo.toml:9:23 | 9 | bar = true | ^^^^ - | "#]]) .run(); @@ -2703,7 +2702,6 @@ fn bad_debuginfo() { | 9 | debug = 'a' | ^^^ - | "#]]) .run(); @@ -2736,7 +2734,6 @@ fn bad_debuginfo2() { | 9 | debug = 3.6 | ^^^ - | "#]]) .run(); @@ -2767,7 +2764,6 @@ fn bad_opt_level() { | 7 | build = 3 | ^ - | "#]]) .run(); @@ -3067,7 +3063,6 @@ fn bad_trim_paths() { | 8 | trim-paths = "split-debuginfo" | ^^^^^^^^^^^^^^^^^ - | "#]]) .run(); diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index 4c44c8836b8..9e63e421ad7 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -423,7 +423,6 @@ fn cargo_compile_with_invalid_manifest2() { | 3 | foo = bar | ^^^ - | "#]]) .run(); @@ -441,7 +440,6 @@ fn cargo_compile_with_invalid_manifest3() { | 1 | a = bar | ^^^ - | "#]]) .run(); @@ -496,7 +494,6 @@ fn cargo_compile_with_invalid_version() { | 4 | version = "1.0" | ^^^^^ - | "#]]) .run(); @@ -516,7 +513,6 @@ fn cargo_compile_with_empty_package_name() { | 3 | name = "" | ^^ - | "#]]) .run(); @@ -536,7 +532,6 @@ fn cargo_compile_with_invalid_package_name() { | 3 | name = "foo@bar" | ^^^^^^^^^ - | "#]]) .run(); @@ -1317,7 +1312,6 @@ fn cargo_compile_with_invalid_dep_rename() { | 8 | "haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | "#]]) .run(); diff --git a/tests/testsuite/cargo_add/invalid_manifest/stderr.term.svg b/tests/testsuite/cargo_add/invalid_manifest/stderr.term.svg index f9c867bc775..611e12f4255 100644 --- a/tests/testsuite/cargo_add/invalid_manifest/stderr.term.svg +++ b/tests/testsuite/cargo_add/invalid_manifest/stderr.term.svg @@ -1,4 +1,4 @@ - +