@@ -45,11 +45,16 @@ use crate::utils::generate_unique_field_name;
4545#[ cfg_attr( test, mutants:: skip) ] // procedural macro API cannot be used in tests directly
4646pub fn error ( _args : TokenStream , input : TokenStream ) -> TokenStream {
4747 let mut input = parse_macro_input ! ( input as DeriveInput ) ;
48+ TokenStream :: from ( error_impl ( & mut input) )
49+ }
4850
49- add_fiasko_error_derive ( & mut input) ;
50- add_ohno_core_field ( & mut input) ;
51+ fn error_impl ( input : & mut DeriveInput ) -> proc_macro2:: TokenStream {
52+ if let Err ( err) = add_ohno_core_field ( input) {
53+ return err. to_compile_error ( ) ;
54+ }
55+ add_fiasko_error_derive ( input) ;
5156
52- TokenStream :: from ( quote ! { #input } )
57+ quote ! { #input }
5358}
5459
5560fn add_fiasko_error_derive ( input : & mut DeriveInput ) {
@@ -61,7 +66,7 @@ fn add_fiasko_error_derive(input: &mut DeriveInput) {
6166 ) ;
6267}
6368
64- fn add_ohno_core_field ( input : & mut DeriveInput ) {
69+ fn add_ohno_core_field ( input : & mut DeriveInput ) -> syn :: Result < ( ) > {
6570 if let Data :: Struct ( data_struct) = & mut input. data {
6671 match & mut data_struct. fields {
6772 Fields :: Unit => {
@@ -95,8 +100,12 @@ fn add_ohno_core_field(input: &mut DeriveInput) {
95100 } ) ;
96101 }
97102 }
103+ Ok ( ( ) )
98104 } else {
99- // Not a struct, can't transform
105+ Err ( syn:: Error :: new_spanned (
106+ & input. ident ,
107+ "#[ohno::error] can only be applied to structs" ,
108+ ) )
100109 }
101110}
102111
@@ -134,7 +143,7 @@ mod tests {
134143 }
135144 } ;
136145
137- crate :: error_type_attr:: add_ohno_core_field ( & mut input) ;
146+ crate :: error_type_attr:: add_ohno_core_field ( & mut input) . unwrap ( ) ;
138147
139148 let expected: proc_macro2:: TokenStream = parse_quote ! {
140149 struct TestError {
@@ -149,19 +158,52 @@ mod tests {
149158
150159 #[ test]
151160 fn test_add_ohno_core_field_with_enum ( ) {
152- // Test that the function doesn't crash when given an enum
153- // This covers line 99 (the else branch for non-struct types)
161+ // Test that the function returns an error when given an enum
154162 let mut input: DeriveInput = parse_quote ! {
155163 enum TestError {
156164 Variant1 ,
157165 Variant2 ,
158166 }
159167 } ;
160168
161- let original = input. to_token_stream ( ) . to_string ( ) ;
162- crate :: error_type_attr:: add_ohno_core_field ( & mut input) ;
169+ let result = crate :: error_type_attr:: add_ohno_core_field ( & mut input) ;
170+ assert ! ( result. is_err( ) ) ;
171+ let err = result. unwrap_err ( ) ;
172+ assert ! ( err. to_string( ) . contains( "#[ohno::error] can only be applied to structs" ) ) ;
173+ }
174+
175+ #[ test]
176+ fn test_add_ohno_core_field_enum_produces_compile_error ( ) {
177+ // Verifies the to_compile_error() path used by the error proc macro (line 50)
178+ let mut input: DeriveInput = parse_quote ! {
179+ enum NotAStruct {
180+ A ,
181+ }
182+ } ;
183+
184+ let err = crate :: error_type_attr:: add_ohno_core_field ( & mut input) . unwrap_err ( ) ;
185+ let compile_error = err. to_compile_error ( ) . to_string ( ) ;
186+
187+ assert ! ( compile_error. contains( "compile_error" ) ) ;
188+ assert ! (
189+ compile_error. contains( "#[ohno::error] can only be applied to structs" ) ,
190+ "compile error should contain the expected message, got: {compile_error}"
191+ ) ;
192+ }
193+
194+ #[ test]
195+ fn test_error_impl_returns_compile_error_for_enum ( ) {
196+ let mut input: DeriveInput = parse_quote ! {
197+ enum NotAStruct {
198+ A ,
199+ }
200+ } ;
201+
202+ let output = crate :: error_type_attr:: error_impl ( & mut input) . to_string ( ) ;
163203
164- // The enum should remain unchanged since we can't transform it
165- assert_eq ! ( input. to_token_stream( ) . to_string( ) , original) ;
204+ assert ! (
205+ output. contains( "compile_error" ) ,
206+ "error_impl should return a compile_error token stream for enums, got: {output}"
207+ ) ;
166208 }
167209}
0 commit comments