From d32127bb5d1eb0143f2e902955f084552f2863d6 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Fri, 17 Nov 2023 03:11:49 -0500 Subject: [PATCH] Remove proc-macro-error dependency (#136) * Don't blindly discard errors This prevents syn::Error from propagating. Updates #135. * Use syn::ParseStream::error This fails tests. https://github.com/frondeus/test-case/issues/135 * Use proc-macro2-diagnostics Bump MSRV to 1.63 for the new yansi dependency (through proc-macro2-diagnostics). See https://github.com/SergioBenitez/yansi/blob/e8247a492e1b/Cargo.toml#L12. --- Cargo.toml | 2 +- crates/test-case-core/Cargo.toml | 10 +++++----- crates/test-case-core/src/complex_expr.rs | 11 ++++++++--- crates/test-case-core/src/expr.rs | 3 ++- crates/test-case-core/src/test_case.rs | 11 +++++++++-- crates/test-case-core/src/test_matrix/mod.rs | 14 ++++++++++++-- crates/test-case-macros/Cargo.toml | 9 ++++----- crates/test-case-macros/src/lib.rs | 2 -- 8 files changed, 41 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c3a17a9..c5d2cfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" repository = "https://github.com/frondeus/test-case" documentation = "https://docs.rs/test-case" exclude = ["tests/snapshots/**/*"] -rust-version = "1.58.1" +rust-version = "1.63" [features] with-regex = ["regex", "test-case-macros/with-regex"] diff --git a/crates/test-case-core/Cargo.toml b/crates/test-case-core/Cargo.toml index a6e1166..e314fe4 100644 --- a/crates/test-case-core/Cargo.toml +++ b/crates/test-case-core/Cargo.toml @@ -22,8 +22,8 @@ doctest = false path = "src/lib.rs" [dependencies] -cfg-if = "1.0" -proc-macro2 = { version = "1.0", features = [] } -proc-macro-error = { version = "1.0", default-features = false } -quote = "1.0" -syn = { version = "2.0", features = ["full", "extra-traits"] } +cfg-if = "1.0" +proc-macro2 = { version = "1.0", features = [] } +proc-macro2-diagnostics = "0.10" +quote = "1.0" +syn = { version = "2.0", features = ["full", "extra-traits"] } diff --git a/crates/test-case-core/src/complex_expr.rs b/crates/test-case-core/src/complex_expr.rs index e708f2e..08a7637 100644 --- a/crates/test-case-core/src/complex_expr.rs +++ b/crates/test-case-core/src/complex_expr.rs @@ -1,5 +1,6 @@ use crate::utils::fmt_syn; use proc_macro2::Group; +use proc_macro2::Span; use proc_macro2::TokenStream; use quote::{quote, TokenStreamExt}; use std::fmt::{Display, Formatter}; @@ -323,11 +324,11 @@ impl ComplexTestCase { expected_regex: input.parse()?, }) } else { - proc_macro_error::abort!(input.span(), "'with-regex' feature is required to use 'matches-regex' keyword"); + return Err(input.error("'with-regex' feature is required to use 'matches-regex' keyword")); } } } else { - proc_macro_error::abort!(input.span(), "cannot parse complex expression") + return Err(input.error("cannot parse complex expression")); }) } } @@ -450,7 +451,11 @@ fn regex_assertion(expected_regex: &Expr) -> TokenStream { fn not_assertion(not: &ComplexTestCase) -> TokenStream { match not { ComplexTestCase::Not(_) => { - proc_macro_error::abort_call_site!("multiple negations on single item are forbidden") + use proc_macro2_diagnostics::SpanDiagnosticExt as _; + + Span::call_site() + .error("multiple negations on single item are forbidden") + .emit_as_expr_tokens() } ComplexTestCase::And(cases) => negate(and_assertion(cases)), ComplexTestCase::Or(cases) => negate(or_assertion(cases)), diff --git a/crates/test-case-core/src/expr.rs b/crates/test-case-core/src/expr.rs index 41b1bcc..4e4cc2f 100644 --- a/crates/test-case-core/src/expr.rs +++ b/crates/test-case-core/src/expr.rs @@ -193,9 +193,10 @@ where Mapping: FnOnce(Option) -> TestCaseResult, Inner: Parse, { + let result = (!input.is_empty()).then(|| input.parse()).transpose()?; Ok(TestCaseExpression { _token: token, extra_keywords, - result: mapping(input.parse::().ok()), + result: mapping(result), }) } diff --git a/crates/test-case-core/src/test_case.rs b/crates/test-case-core/src/test_case.rs index bb37311..376b14e 100644 --- a/crates/test-case-core/src/test_case.rs +++ b/crates/test-case-core/src/test_case.rs @@ -17,8 +17,15 @@ pub struct TestCase { impl Parse for TestCase { fn parse(input: ParseStream) -> Result { let args = Punctuated::parse_separated_nonempty_with(input, Expr::parse)?; - let expression = input.parse::().ok(); - let comment = input.parse::().ok(); + let expression = (!input.is_empty()).then(|| input.parse()).transpose(); + let comment = (!input.is_empty()).then(|| input.parse()).transpose(); + // if both are errors, pick the expression error since it is more likely to be informative. + // + // TODO(https://github.com/frondeus/test-case/issues/135): avoid Result::ok entirely. + let (expression, comment) = match (expression, comment) { + (Err(expression), Err(_comment)) => return Err(expression), + (expression, comment) => (expression.ok().flatten(), comment.ok().flatten()), + }; Ok(Self::new_from_parsed(args, expression, comment)) } diff --git a/crates/test-case-core/src/test_matrix/mod.rs b/crates/test-case-core/src/test_matrix/mod.rs index 889c3c3..310c3c9 100644 --- a/crates/test-case-core/src/test_matrix/mod.rs +++ b/crates/test-case-core/src/test_matrix/mod.rs @@ -46,9 +46,19 @@ impl Parse for TestMatrix { fn parse(input: ParseStream) -> syn::Result { let args: Punctuated = Punctuated::parse_separated_nonempty(input)?; + let expression = (!input.is_empty()).then(|| input.parse()).transpose(); + let comment = (!input.is_empty()).then(|| input.parse()).transpose(); + // if both are errors, pick the expression error since it is more likely to be informative. + // + // TODO(https://github.com/frondeus/test-case/issues/135): avoid Result::ok entirely. + let (expression, comment) = match (expression, comment) { + (Err(expression), Err(_comment)) => return Err(expression), + (expression, comment) => (expression.ok().flatten(), comment.ok().flatten()), + }; + let mut matrix = TestMatrix { - expression: input.parse().ok(), - comment: input.parse().ok(), + expression, + comment, ..Default::default() }; diff --git a/crates/test-case-macros/Cargo.toml b/crates/test-case-macros/Cargo.toml index 14111cb..dd9bd4d 100644 --- a/crates/test-case-macros/Cargo.toml +++ b/crates/test-case-macros/Cargo.toml @@ -23,8 +23,7 @@ proc-macro = true path = "src/lib.rs" [dependencies] -proc-macro2 = { version = "1.0", features = [] } -proc-macro-error = { version = "1.0", default-features = false } -quote = "1.0" -syn = { version = "2.0", features = ["full", "extra-traits", "parsing"] } -test-case-core = { version = "3.2.1", path = "../test-case-core", default-features = false } +proc-macro2 = { version = "1.0", features = [] } +quote = "1.0" +syn = { version = "2.0", features = ["full", "extra-traits", "parsing"] } +test-case-core = { version = "3.2.1", path = "../test-case-core", default-features = false } diff --git a/crates/test-case-macros/src/lib.rs b/crates/test-case-macros/src/lib.rs index 82561da..90d4675 100644 --- a/crates/test-case-macros/src/lib.rs +++ b/crates/test-case-macros/src/lib.rs @@ -22,7 +22,6 @@ use test_case_core::{TestCase, TestMatrix}; /// When _expected result_ is provided, it is compared against the actual value generated with _test body_ using `assert_eq!`. /// _Test cases_ that don't provide _expected result_ should contain custom assertions within _test body_ or return `Result` similar to `#[test]` macro. #[proc_macro_attribute] -#[proc_macro_error::proc_macro_error] pub fn test_case(args: TokenStream, input: TokenStream) -> TokenStream { let test_case = parse_macro_input!(args as TestCase); let mut item = parse_macro_input!(input as ItemFn); @@ -50,7 +49,6 @@ pub fn test_case(args: TokenStream, input: TokenStream) -> TokenStream { /// _Expected result_ and _Test body_ are the same as they are for the singular `#[test_case(...)]` /// macro but are applied to every case generated by `#[test_matrix(...)]`. #[proc_macro_attribute] -#[proc_macro_error::proc_macro_error] pub fn test_matrix(args: TokenStream, input: TokenStream) -> TokenStream { let matrix = parse_macro_input!(args as TestMatrix); let mut item = parse_macro_input!(input as ItemFn);