diff --git a/Cargo.lock b/Cargo.lock index a295b9a0508..eed83df86b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,6 +192,15 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "backtrace-ext" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" +dependencies = [ + "backtrace", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -222,7 +231,16 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", ] [[package]] @@ -231,6 +249,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -493,8 +517,8 @@ dependencies = [ "guppy-workspace-hack", "hakari", "log", - "owo-colors", - "supports-color", + "owo-colors 3.5.0", + "supports-color 1.3.1", "tempfile", ] @@ -672,7 +696,7 @@ dependencies = [ "anstyle", "clap_lex 0.7.2", "strsim 0.11.1", - "terminal_size", + "terminal_size 0.4.1", ] [[package]] @@ -743,7 +767,7 @@ dependencies = [ "eyre", "indenter", "once_cell", - "owo-colors", + "owo-colors 3.5.0", ] [[package]] @@ -981,6 +1005,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "datatest-stable" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833306ca7eec4d95844e65f0d7502db43888c5c1006c6c517e8cf51a27d15431" +dependencies = [ + "camino", + "fancy-regex", + "libtest-mimic", + "walkdir", +] + [[package]] name = "dbus" version = "0.9.7" @@ -1226,6 +1262,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "escape8259" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" + [[package]] name = "eyre" version = "0.6.12" @@ -1248,6 +1290,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fancy-regex" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" +dependencies = [ + "bit-set 0.8.0", + "regex-automata 0.4.5", + "regex-syntax 0.8.2", +] + [[package]] name = "faster-hex" version = "0.9.0" @@ -2279,15 +2332,19 @@ name = "guppy-workspace-hack" version = "0.1.0" dependencies = [ "aho-corasick", + "backtrace", + "camino", "clap 4.5.22", "clap_builder", + "console", "getrandom", "indexmap 1.9.3", "libc", "log", + "miette", "num-traits", "once_cell", - "owo-colors", + "owo-colors 3.5.0", "petgraph", "proc-macro2", "quote", @@ -2299,6 +2356,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.90", + "textwrap 0.16.0", "toml 0.5.11", "winapi", "windows-sys 0.52.0", @@ -2321,7 +2379,7 @@ dependencies = [ "include_dir", "indenter", "itertools 0.13.0", - "owo-colors", + "owo-colors 3.5.0", "pathdiff", "proptest", "proptest-derive", @@ -2545,6 +2603,18 @@ version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" +[[package]] +name = "insta" +version = "1.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "similar", +] + [[package]] name = "is-terminal" version = "0.4.10" @@ -2677,7 +2747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -2721,6 +2791,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libtest-mimic" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5297962ef19edda4ce33aaa484386e0a5b3d7f2f4e037cbeee00503ef6b29d33" +dependencies = [ + "anstream", + "anstyle", + "clap 4.5.22", + "escape8259", +] + [[package]] name = "libz-sys" version = "1.1.15" @@ -2733,6 +2815,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -2796,8 +2884,16 @@ version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" dependencies = [ + "backtrace", + "backtrace-ext", "cfg-if", "miette-derive", + "owo-colors 4.1.0", + "supports-color 3.0.2", + "supports-hyperlinks", + "supports-unicode", + "terminal_size 0.3.0", + "textwrap 0.16.0", "thiserror 1.0.69", "unicode-width 0.1.13", ] @@ -3024,9 +3120,15 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" dependencies = [ - "supports-color", + "supports-color 1.3.1", ] +[[package]] +name = "owo-colors" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" + [[package]] name = "p384" version = "0.13.0" @@ -3246,8 +3348,8 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ - "bit-set", - "bit-vec", + "bit-set 0.5.3", + "bit-vec 0.6.3", "bitflags 2.6.0", "lazy_static", "num-traits", @@ -3714,6 +3816,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" + [[package]] name = "sized-chunks" version = "0.6.5" @@ -3730,6 +3838,12 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "socket2" version = "0.5.7" @@ -3823,6 +3937,15 @@ dependencies = [ "is_ci", ] +[[package]] +name = "supports-color" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" +dependencies = [ + "is_ci", +] + [[package]] name = "supports-hyperlinks" version = "3.0.0" @@ -3903,7 +4026,9 @@ dependencies = [ name = "target-spec-miette" version = "0.4.0" dependencies = [ + "datatest-stable", "guppy-workspace-hack", + "insta", "miette", "target-spec", ] @@ -3929,6 +4054,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "terminal_size" version = "0.4.1" @@ -3986,6 +4121,11 @@ name = "textwrap" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width 0.1.13", +] [[package]] name = "thiserror" @@ -4284,6 +4424,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-normalization" version = "0.1.22" diff --git a/Cargo.toml b/Cargo.toml index 0d9ed6b8bdf..842f01d2afd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,9 @@ members = [ [workspace.dependencies] ahash = "0.8.11" +datatest-stable = "0.2.10" guppy-workspace-hack = "0.1.0" +insta = "1.41.1" miette = "7.2.0" [workspace.package] diff --git a/target-spec-miette/Cargo.toml b/target-spec-miette/Cargo.toml index e77f9d8bbae..a1dff5205a4 100644 --- a/target-spec-miette/Cargo.toml +++ b/target-spec-miette/Cargo.toml @@ -16,5 +16,15 @@ target-spec = { version = "3.2.2", path = "../target-spec" } miette.workspace = true guppy-workspace-hack.workspace = true +[dev-dependencies] +datatest-stable.workspace = true +insta.workspace = true +miette = { workspace = true, features = ["fancy"] } +target-spec = { version = "3.2.2", path = "../target-spec", features = ["custom"] } + [lints] workspace = true + +[[test]] +name = "snapshot" +harness = false diff --git a/target-spec-miette/src/lib.rs b/target-spec-miette/src/lib.rs index 5e7a6e768a7..378e6b53ac2 100644 --- a/target-spec-miette/src/lib.rs +++ b/target-spec-miette/src/lib.rs @@ -15,10 +15,11 @@ #![forbid(unsafe_code)] #![cfg_attr(doc_cfg, feature(doc_cfg, doc_auto_cfg))] -use miette::{Diagnostic, LabeledSpan, SourceCode}; +use miette::{Diagnostic, LabeledSpan, SourceCode, SourceOffset, SourceSpan}; use std::{error::Error as StdError, fmt}; use target_spec::errors::{ - Error as TargetSpecError, ExpressionParseError, PlainStringParseError, TripleParseError, + CustomTripleCreateError, Error as TargetSpecError, ExpressionParseError, PlainStringParseError, + TripleParseError, }; /// Extension trait that converts errors into a [`miette::Diagnostic`]. @@ -40,6 +41,9 @@ impl IntoMietteDiagnostic for TargetSpecError { Self::InvalidExpression(error) => Box::new(error.into_diagnostic()), Self::InvalidTargetSpecString(error) => Box::new(error.into_diagnostic()), Self::UnknownPlatformTriple(error) => Box::new(error.into_diagnostic()), + #[allow(deprecated)] + Self::CustomTripleCreate(error) => Box::new(error.into_diagnostic()), + Self::CustomPlatformCreate(error) => Box::new(error.into_diagnostic()), other => Box::::from(other.to_string()), } } @@ -211,3 +215,60 @@ impl IntoMietteDiagnostic for PlainStringParseError { PlainStringParseDiagnostic::new(self) } } + +impl IntoMietteDiagnostic for CustomTripleCreateError { + type IntoDiagnostic = CustomTripleCreateDiagnostic; + + fn into_diagnostic(self) -> Self::IntoDiagnostic { + CustomTripleCreateDiagnostic::new(self) + } +} + +/// A wrapper around [`CustomTripleCreateError`] that implements [`Diagnostic`]. +pub struct CustomTripleCreateDiagnostic(CustomTripleCreateError); + +impl CustomTripleCreateDiagnostic { + /// Creates a new `CustomTripleCreateDiagnostic`. + pub fn new(error: CustomTripleCreateError) -> Self { + Self(error) + } +} + +impl fmt::Debug for CustomTripleCreateDiagnostic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl fmt::Display for CustomTripleCreateDiagnostic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl StdError for CustomTripleCreateDiagnostic { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.0.source() + } +} + +impl Diagnostic for CustomTripleCreateDiagnostic { + fn source_code(&self) -> Option<&dyn SourceCode> { + self.0.input_string().map(|input| input as &dyn SourceCode) + } + + fn labels(&self) -> Option + '_>> { + // ughhh, clippy warns about `?` here but I don't like it: + // https://github.com/rust-lang/rust-clippy/issues/13804 + let input = self.0.input()?; + let (line, column) = self.0.line_and_column()?; + + let source_offset = SourceOffset::from_location(input, line, column); + // serde_json doesn't return the span of the error, just a single + // offset. + let span = SourceSpan::new(source_offset, 0); + + let label = LabeledSpan::new_with_span(self.0.label(), span); + Some(Box::new(std::iter::once(label))) + } +} diff --git a/target-spec-miette/tests/snapshot/custom-invalid/input/invalid-arch.json b/target-spec-miette/tests/snapshot/custom-invalid/input/invalid-arch.json new file mode 100644 index 00000000000..162183cf791 --- /dev/null +++ b/target-spec-miette/tests/snapshot/custom-invalid/input/invalid-arch.json @@ -0,0 +1,57 @@ +{ + "arch": 123, + "cpu": "x86-64", + "crt-objects-fallback": "false", + "crt-static-respected": true, + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + "dynamic-linking": true, + "env": "gnu", + "has-rpath": true, + "has-thread-local": true, + "is-builtin": true, + "linker-flavor": "gnu-cc", + "llvm-target": "x86_64-unknown-linux-gnu", + "max-atomic-width": 64, + "metadata": { + "description": "64-bit Linux (kernel 3.2+, glibc 2.17+)", + "host_tools": true, + "std": true, + "tier": 1 + }, + "os": "linux", + "plt-by-default": false, + "position-independent-executables": true, + "pre-link-args": { + "gnu-cc": [ + "-m64" + ], + "gnu-lld-cc": [ + "-m64" + ] + }, + "relro-level": "full", + "stack-probes": { + "kind": "inline" + }, + "static-position-independent-executables": true, + "supported-sanitizers": [ + "address", + "leak", + "memory", + "thread", + "cfi", + "kcfi", + "safestack", + "dataflow" + ], + "supported-split-debuginfo": [ + "packed", + "unpacked", + "off" + ], + "target-pointer-width": "xx", + "supports-xray": true, + "target-family": [ + "unix" + ] +} \ No newline at end of file diff --git a/target-spec-miette/tests/snapshot/custom-invalid/input/invalid-target-pointer-width.json b/target-spec-miette/tests/snapshot/custom-invalid/input/invalid-target-pointer-width.json new file mode 100644 index 00000000000..cb3492e44b8 --- /dev/null +++ b/target-spec-miette/tests/snapshot/custom-invalid/input/invalid-target-pointer-width.json @@ -0,0 +1,57 @@ +{ + "arch": "x86_64", + "cpu": "x86-64", + "crt-objects-fallback": "false", + "crt-static-respected": true, + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + "dynamic-linking": true, + "env": "gnu", + "has-rpath": true, + "has-thread-local": true, + "is-builtin": true, + "linker-flavor": "gnu-cc", + "llvm-target": "x86_64-unknown-linux-gnu", + "max-atomic-width": 64, + "metadata": { + "description": "64-bit Linux (kernel 3.2+, glibc 2.17+)", + "host_tools": true, + "std": true, + "tier": 1 + }, + "os": "linux", + "plt-by-default": false, + "position-independent-executables": true, + "pre-link-args": { + "gnu-cc": [ + "-m64" + ], + "gnu-lld-cc": [ + "-m64" + ] + }, + "relro-level": "full", + "stack-probes": { + "kind": "inline" + }, + "static-position-independent-executables": true, + "supported-sanitizers": [ + "address", + "leak", + "memory", + "thread", + "cfi", + "kcfi", + "safestack", + "dataflow" + ], + "supported-split-debuginfo": [ + "packed", + "unpacked", + "off" + ], + "target-pointer-width": "xx", + "supports-xray": true, + "target-family": [ + "unix" + ] +} \ No newline at end of file diff --git a/target-spec-miette/tests/snapshot/custom-invalid/input/missing-arch.json b/target-spec-miette/tests/snapshot/custom-invalid/input/missing-arch.json new file mode 100644 index 00000000000..e30e7aba5ff --- /dev/null +++ b/target-spec-miette/tests/snapshot/custom-invalid/input/missing-arch.json @@ -0,0 +1,35 @@ +{ + "abi-return-struct-as-int": true, + "cpu": "ppc64", + "crt-objects-fallback": "false", + "crt-static-respected": true, + "data-layout": "E-m:e-Fn32-i64:64-n32:64", + "default-dwarf-version": 2, + "dynamic-linking": true, + "has-rpath": true, + "has-thread-local": true, + "is-builtin": true, + "linker-flavor": "gnu-cc", + "llvm-target": "powerpc64-unknown-freebsd", + "max-atomic-width": 64, + "metadata": { + "description": "PPC64 FreeBSD (ELFv1 and ELFv2)", + "host_tools": true, + "std": true, + "tier": 3 + }, + "os": "freebsd", + "position-independent-executables": true, + "pre-link-args": { + "gnu-cc": ["-m64"], + "gnu-lld-cc": ["-m64"] + }, + "relro-level": "full", + "stack-probes": { + "kind": "inline" + }, + "target-endian": "big", + "target-family": ["unix"], + "target-mcount": "_mcount", + "target-pointer-width": "64" +} diff --git a/target-spec-miette/tests/snapshot/custom-invalid/input/syntax-error.json b/target-spec-miette/tests/snapshot/custom-invalid/input/syntax-error.json new file mode 100644 index 00000000000..f38f9194057 --- /dev/null +++ b/target-spec-miette/tests/snapshot/custom-invalid/input/syntax-error.json @@ -0,0 +1,40 @@ +{ + "arch": "x86_64", + "cpu": "x86-64", + "crt-objects-fallback": "false", + "crt-static-respected": true, + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + "dynamic-linking": true, + "env": "gnu", + "has-rpath": true, + "has-thread-local": true, + "is-builtin": true, + "linker-flavor": "gnu-cc", + "llvm-target": "x86_64-unknown-linux-gnu", + "max-atomic-width": 64, + "metadata": { + "description": "64-bit Linux (kernel 3.2+, glibc 2.17+)", + "host_tools": true, + "std": true, + "tier": 1 + }, + "os": "linux", + "plt-by-default": false, + "position-independent-executables": true, + "pre-link-args": { + "gnu-cc": [ + "-m64" + ], + "gnu-lld-cc": [ + "-m64" + ] + }, + "relro-level": "full", + "stack-probes": { + "kind": "inline" + }, + "static-position-independent-executables": true, + "supported-sanitizers": [ + "address", + "leak", + "memory", \ No newline at end of file diff --git a/target-spec-miette/tests/snapshot/custom-invalid/output/invalid-arch.json-display.snap b/target-spec-miette/tests/snapshot/custom-invalid/output/invalid-arch.json-display.snap new file mode 100644 index 00000000000..e3eb2c41c9d --- /dev/null +++ b/target-spec-miette/tests/snapshot/custom-invalid/output/invalid-arch.json-display.snap @@ -0,0 +1,14 @@ +--- +source: target-spec-miette/tests/snapshot/custom.rs +expression: "format!(\"{:?}\", miette::Report::new_boxed(diagnostic))" +snapshot_kind: text +--- + × error deserializing custom target JSON for `my-target` + ╰─▶ invalid type: integer `123`, expected a string at line 2 column 13 + ╭─[2:13] + 1 │ { + 2 │ "arch": 123, + · ▲ + · ╰── invalid type: integer `123`, expected a string + 3 │ "cpu": "x86-64", + ╰──── diff --git a/target-spec-miette/tests/snapshot/custom-invalid/output/invalid-target-pointer-width.json-display.snap b/target-spec-miette/tests/snapshot/custom-invalid/output/invalid-target-pointer-width.json-display.snap new file mode 100644 index 00000000000..f812f21a7ee --- /dev/null +++ b/target-spec-miette/tests/snapshot/custom-invalid/output/invalid-target-pointer-width.json-display.snap @@ -0,0 +1,15 @@ +--- +source: target-spec-miette/tests/snapshot/custom.rs +expression: "format!(\"{:?}\", miette::Report::new_boxed(diagnostic))" +snapshot_kind: text +--- + × error deserializing custom target JSON for `my-target` + ╰─▶ error parsing as integer: invalid digit found in string at line 52 + column 30 + ╭─[52:30] + 51 │ ], + 52 │ "target-pointer-width": "xx", + · ▲ + · ╰── error parsing as integer: invalid digit found in string + 53 │ "supports-xray": true, + ╰──── diff --git a/target-spec-miette/tests/snapshot/custom-invalid/output/missing-arch.json-display.snap b/target-spec-miette/tests/snapshot/custom-invalid/output/missing-arch.json-display.snap new file mode 100644 index 00000000000..209010d311e --- /dev/null +++ b/target-spec-miette/tests/snapshot/custom-invalid/output/missing-arch.json-display.snap @@ -0,0 +1,13 @@ +--- +source: target-spec-miette/tests/snapshot/custom.rs +expression: "format!(\"{:?}\", miette::Report::new_boxed(diagnostic))" +snapshot_kind: text +--- + × error deserializing custom target JSON for `my-target` + ╰─▶ missing field `arch` at line 35 column 1 + ╭─[35:1] + 34 │ "target-pointer-width": "64" + 35 │ } + · ▲ + · ╰── missing field `arch` + ╰──── diff --git a/target-spec-miette/tests/snapshot/custom-invalid/output/syntax-error.json-display.snap b/target-spec-miette/tests/snapshot/custom-invalid/output/syntax-error.json-display.snap new file mode 100644 index 00000000000..f5aa6349a37 --- /dev/null +++ b/target-spec-miette/tests/snapshot/custom-invalid/output/syntax-error.json-display.snap @@ -0,0 +1,13 @@ +--- +source: target-spec-miette/tests/snapshot/custom.rs +expression: "format!(\"{:?}\", miette::Report::new_boxed(diagnostic))" +snapshot_kind: text +--- + × error deserializing custom target JSON for `my-target` + ╰─▶ EOF while parsing a value at line 40 column 13 + ╭─[40:13] + 39 │ "leak", + 40 │ "memory", + · ▲ + · ╰── EOF while parsing a value + ╰──── diff --git a/target-spec-miette/tests/snapshot/custom.rs b/target-spec-miette/tests/snapshot/custom.rs new file mode 100644 index 00000000000..0ba69623500 --- /dev/null +++ b/target-spec-miette/tests/snapshot/custom.rs @@ -0,0 +1,47 @@ +// Copyright (c) The cargo-guppy Contributors +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Tests for custom platforms with miette. +//! +//! These tests live here because they depend on target-spec with the custom +//! feature enabled, as well as target-spec-miette. + +use datatest_stable::Utf8Path; +use insta::internals::SettingsBindDropGuard; +use target_spec::TargetFeatures; +use target_spec_miette::IntoMietteDiagnostic; + +pub(crate) fn test_invalid_custom_json( + path: &Utf8Path, + contents: String, +) -> datatest_stable::Result<()> { + let (_guard, insta_prefix) = bind_insta_settings(path); + + let error = target_spec::Platform::new_custom("my-target", &contents, TargetFeatures::none()) + .expect_err("expected input to fail"); + + let diagnostic = error.into_diagnostic(); + insta::assert_snapshot!( + format!("{insta_prefix}-display"), + // This displays fancy output. Note the use of assert_snapshot, not + // assert_debug_snapshot, since the latter uses the pretty-printed Debug + // (which doesn't do what you would expect with miette/anyhow etc). + format!("{:?}", miette::Report::new_boxed(diagnostic)), + ); + + Ok(()) +} + +/// Binds insta settings for a test, and returns the prefix to use for snapshots. +fn bind_insta_settings(path: &Utf8Path) -> (SettingsBindDropGuard, &str) { + let mut settings = insta::Settings::clone_current(); + // Make insta suitable for datatest-stable use. + settings.set_input_file(path); + settings.set_snapshot_path("custom-invalid/output"); + settings.set_prepend_module_to_snapshot(false); + + let guard = settings.bind_to_scope(); + let insta_prefix = path.file_name().unwrap(); + + (guard, insta_prefix) +} diff --git a/target-spec-miette/tests/snapshot/main.rs b/target-spec-miette/tests/snapshot/main.rs new file mode 100644 index 00000000000..f969d249305 --- /dev/null +++ b/target-spec-miette/tests/snapshot/main.rs @@ -0,0 +1,12 @@ +// Copyright (c) The cargo-guppy Contributors +// SPDX-License-Identifier: MIT OR Apache-2.0 + +// TODO: add snapshot tests for the other errors as well. + +mod custom; + +datatest_stable::harness!( + custom::test_invalid_custom_json, + "tests/snapshot/custom-invalid/input", + r"^.*/*", +); diff --git a/target-spec/src/errors.rs b/target-spec/src/errors.rs index 56cf02e62db..f757e2c93c7 100644 --- a/target-spec/src/errors.rs +++ b/target-spec/src/errors.rs @@ -15,8 +15,11 @@ pub enum Error { InvalidTargetSpecString(PlainStringParseError), /// The provided platform triple was unknown. UnknownPlatformTriple(TripleParseError), - /// An error occurred while creating a custom triple (in the position that a `cfg()` expression - /// would be). + /// Deprecated: this variant is no longer used. + #[deprecated( + since = "3.3.0", + note = "this variant is no longer returned: instead, use CustomPlatformCreate" + )] CustomTripleCreate(CustomTripleCreateError), /// An error occurred while creating a custom platform. CustomPlatformCreate(CustomTripleCreateError), @@ -32,6 +35,7 @@ impl fmt::Display for Error { Error::UnknownPlatformTriple(_) => { write!(f, "unknown platform triple") } + #[allow(deprecated)] Error::CustomTripleCreate(_) => write!(f, "error creating custom triple"), Error::CustomPlatformCreate(_) => { write!(f, "error creating custom platform") @@ -46,6 +50,7 @@ impl error::Error for Error { Error::InvalidExpression(err) => Some(err), Error::InvalidTargetSpecString(err) => Some(err), Error::UnknownPlatformTriple(err) => Some(err), + #[allow(deprecated)] Error::CustomTripleCreate(err) => Some(err), Error::CustomPlatformCreate(err) => Some(err), } @@ -315,6 +320,24 @@ impl error::Error for TripleParseErrorKind { pub enum CustomTripleCreateError { #[cfg(feature = "custom")] /// An error occurred while deserializing serde data. + DeserializeJson { + /// The specified triple. + triple: String, + + /// The input string that caused the error. + input: String, + + /// The deserialization error that occurred. + error: std::sync::Arc, + }, + + #[cfg(feature = "custom")] + /// Deprecated, and no longer used: instead, use [`Self::DeserializeJson`]. + #[deprecated( + since = "3.3.0", + note = "this variant is no longer returned: instead, \ + use DeserializeJson which also includes the input string" + )] Deserialize { /// The specified triple. triple: String, @@ -330,11 +353,76 @@ pub enum CustomTripleCreateError { Unavailable, } +impl CustomTripleCreateError { + /// Returns the provided input that caused the error, if available. + #[inline] + pub fn input(&self) -> Option<&str> { + self.input_string().map(String::as_str) + } + + /// A version of [`Self::input`] that returns a `&String` rather than a + /// `&str`. + /// + /// This is a workaround for a miette limitation -- `&str` can't be cast to + /// `&dyn SourceCode`, but `&String` can. + pub fn input_string(&self) -> Option<&String> { + match self { + #[cfg(feature = "custom")] + Self::DeserializeJson { input, .. } => Some(input), + #[cfg(feature = "custom")] + #[allow(deprecated)] + Self::Deserialize { .. } => None, + Self::Unavailable => None, + } + } + + /// Returns the line and column number that caused the error, if available + /// and the error is not an I/O error. + /// + /// The line and column number are 1-based, though the column number can be + /// 0 if the error occurred between lines. + #[inline] + pub fn line_and_column(&self) -> Option<(usize, usize)> { + match self { + #[cfg(feature = "custom")] + Self::DeserializeJson { error, .. } => Some((error.line(), error.column())), + #[cfg(feature = "custom")] + #[allow(deprecated)] + Self::Deserialize { .. } => None, + Self::Unavailable => None, + } + } + + /// Returns a label suitable for the error message to label at + /// [`Self::line_and_column`]. + /// + /// This label drops line and column information if available. + pub fn label(&self) -> Option { + match self { + #[cfg(feature = "custom")] + Self::DeserializeJson { error, .. } => { + let label = error.to_string(); + // serde_json appends " at line M column N" -- remove it. + let trimmed = match label.rfind(" at line ") { + Some(idx) => label[..idx].to_string(), + None => label, + }; + Some(trimmed) + } + #[cfg(feature = "custom")] + #[allow(deprecated)] + Self::Deserialize { .. } => None, + Self::Unavailable => None, + } + } +} + impl fmt::Display for CustomTripleCreateError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { #[cfg(feature = "custom")] - Self::Deserialize { triple, .. } => { + #[allow(deprecated)] + Self::DeserializeJson { triple, .. } | Self::Deserialize { triple, .. } => { write!(f, "error deserializing custom target JSON for `{triple}`") } Self::Unavailable => { @@ -348,7 +436,8 @@ impl error::Error for CustomTripleCreateError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { #[cfg(feature = "custom")] - Self::Deserialize { error, .. } => Some(error), + #[allow(deprecated)] + Self::DeserializeJson { error, .. } | Self::Deserialize { error, .. } => Some(error), Self::Unavailable => None, } } diff --git a/target-spec/src/triple.rs b/target-spec/src/triple.rs index 1c605cb64f6..e9aa7a43132 100644 --- a/target-spec/src/triple.rs +++ b/target-spec/src/triple.rs @@ -77,8 +77,9 @@ impl Triple { let triple_str = triple_str.into(); let target_def: TargetDefinition = serde_json::from_str(json).map_err(|error| { - crate::errors::CustomTripleCreateError::Deserialize { + crate::errors::CustomTripleCreateError::DeserializeJson { triple: triple_str.to_string(), + input: json.to_string(), error: error.into(), } })?; diff --git a/workspace-hack/Cargo.toml b/workspace-hack/Cargo.toml index 6067f965c8b..82ac3b703dd 100644 --- a/workspace-hack/Cargo.toml +++ b/workspace-hack/Cargo.toml @@ -18,20 +18,25 @@ edition = "2021" ### BEGIN HAKARI SECTION [dependencies] aho-corasick = { version = "1.1.2" } +backtrace = { version = "0.3.69", features = ["gimli-symbolize"] } +camino = { version = "1.1.9", default-features = false, features = ["serde1"] } clap = { version = "4.5.22", features = ["derive"] } clap_builder = { version = "4.5.22", default-features = false, features = ["color", "help", "std", "suggestions", "usage"] } +console = { version = "0.15.8" } getrandom = { version = "0.2.12", default-features = false, features = ["std"] } indexmap = { version = "1.9.3", default-features = false, features = ["std"] } log = { version = "0.4.22", default-features = false, features = ["std"] } +miette = { version = "7.2.0", features = ["fancy"] } num-traits = { version = "0.2.17", features = ["libm"] } owo-colors = { version = "3.5.0", default-features = false, features = ["supports-colors"] } petgraph = { version = "0.6.5", default-features = false, features = ["graphmap"] } regex = { version = "1.10.5", default-features = false, features = ["perf", "std"] } -regex-automata = { version = "0.4.5", default-features = false, features = ["dfa-onepass", "hybrid", "meta", "nfa", "perf"] } +regex-automata = { version = "0.4.5", default-features = false, features = ["dfa", "hybrid", "meta", "nfa", "perf", "unicode"] } regex-syntax = { version = "0.8.2" } semver = { version = "1.0.23", features = ["serde"] } serde = { version = "1.0.215", features = ["alloc", "derive"] } serde_json = { version = "1.0.133", features = ["unbounded_depth"] } +textwrap = { version = "0.16.0" } toml = { version = "0.5.11", features = ["preserve_order"] } [build-dependencies]