thread_aware_macros_impl/
field_attrs.rs1use syn::{Attribute, Expr, Type};
5
6#[derive(Default, Debug)]
8pub struct FieldAttrCfg {
9 pub skip: bool,
11}
12
13#[expect(clippy::missing_errors_doc, reason = "syn::internal API, no need for docs")]
15pub fn parse_field_attrs(attrs: &[Attribute]) -> syn::Result<FieldAttrCfg> {
16 let mut cfg = FieldAttrCfg::default();
17 for attr in attrs.iter().filter(|a| a.path().is_ident("thread_aware")) {
18 let parsed = attr.parse_args_with(|input: syn::parse::ParseStream| {
19 if input.is_empty() {
20 return Ok(None);
21 }
22 let expr: Expr = input.parse()?;
23 Ok(Some(expr))
24 })?;
25 if let Some(expr) = parsed {
26 match expr {
27 Expr::Path(p) if p.path.is_ident("skip") => {
28 if cfg.skip {
29 return Err(syn::Error::new_spanned(p, "duplicate 'skip'"));
30 }
31 cfg.skip = true;
32 }
33 other => {
34 return Err(syn::Error::new_spanned(
35 other,
36 "unknown thread_aware attribute (only 'skip' is supported)",
37 ));
38 }
39 }
40 }
41 }
42 Ok(cfg)
43}
44
45#[must_use]
47pub fn is_phantom_data(ty: &Type) -> bool {
48 if let Type::Path(tp) = ty
49 && let Some(seg) = tp.path.segments.last()
50 {
51 return seg.ident == "PhantomData";
52 }
53 false
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use syn::parse_quote;
60
61 #[test]
62 fn test_parse_field_attrs_no_attrs() {
63 let attrs: Vec<Attribute> = vec![];
65 let result = parse_field_attrs(&attrs).unwrap();
66 assert!(!result.skip);
67 }
68
69 #[test]
70 fn test_parse_field_attrs_skip() {
71 let attrs: Vec<Attribute> = vec![parse_quote! { #[thread_aware(skip)] }];
73 let result = parse_field_attrs(&attrs).unwrap();
74 assert!(result.skip);
75 }
76
77 #[test]
78 fn test_parse_field_attrs_empty_thread_aware() {
79 let attrs: Vec<Attribute> = vec![parse_quote! { #[thread_aware()] }];
81 let result = parse_field_attrs(&attrs).unwrap();
82 assert!(!result.skip);
83 }
84
85 #[test]
86 fn test_parse_field_attrs_duplicate_skip() {
87 let attrs: Vec<Attribute> = vec![parse_quote! { #[thread_aware(skip)] }, parse_quote! { #[thread_aware(skip)] }];
89 let result = parse_field_attrs(&attrs);
90 assert!(result.is_err());
91 assert!(result.unwrap_err().to_string().contains("duplicate 'skip'"));
92 }
93
94 #[test]
95 fn test_parse_field_attrs_unknown_attribute() {
96 let attrs: Vec<Attribute> = vec![parse_quote! { #[thread_aware(unknown)] }];
98 let result = parse_field_attrs(&attrs);
99 assert!(result.is_err());
100 assert!(result.unwrap_err().to_string().contains("unknown thread_aware attribute"));
101 }
102
103 #[test]
104 fn test_parse_field_attrs_unknown_attribute_with_value() {
105 let attrs: Vec<Attribute> = vec![parse_quote! { #[thread_aware(skip = helper)] }];
107 let result = parse_field_attrs(&attrs);
108 assert!(result.is_err());
109 assert!(result.unwrap_err().to_string().contains("unknown thread_aware attribute"));
110 }
111
112 #[test]
113 fn test_parse_field_attrs_non_thread_aware() {
114 let attrs: Vec<Attribute> = vec![parse_quote! { #[derive(Debug)] }, parse_quote! { #[serde(skip)] }];
116 let result = parse_field_attrs(&attrs).unwrap();
117 assert!(!result.skip);
118 }
119
120 #[test]
121 fn test_parse_field_attrs_mixed_attributes() {
122 let attrs: Vec<Attribute> = vec![
124 parse_quote! { #[derive(Debug)] },
125 parse_quote! { #[thread_aware(skip)] },
126 parse_quote! { #[serde(skip)] },
127 ];
128 let result = parse_field_attrs(&attrs).unwrap();
129 assert!(result.skip);
130 }
131
132 #[test]
133 fn test_is_phantom_data_simple() {
134 let ty: Type = parse_quote! { PhantomData<T> };
136 assert!(is_phantom_data(&ty));
137 }
138
139 #[test]
140 fn test_is_phantom_data_with_std() {
141 let ty: Type = parse_quote! { std::marker::PhantomData<T> };
143 assert!(is_phantom_data(&ty));
144 }
145
146 #[test]
147 fn test_is_phantom_data_with_core() {
148 let ty: Type = parse_quote! { core::marker::PhantomData<T> };
150 assert!(is_phantom_data(&ty));
151 }
152
153 #[test]
154 fn test_is_phantom_data_fully_qualified() {
155 let ty: Type = parse_quote! { ::std::marker::PhantomData<T> };
157 assert!(is_phantom_data(&ty));
158 }
159
160 #[test]
161 fn test_is_phantom_data_multiple_generics() {
162 let ty: Type = parse_quote! { PhantomData<(T, U, V)> };
164 assert!(is_phantom_data(&ty));
165 }
166
167 #[test]
168 fn test_is_phantom_data_not_phantom() {
169 let ty: Type = parse_quote! { String };
171 assert!(!is_phantom_data(&ty));
172
173 let ty: Type = parse_quote! { Vec<u8> };
174 assert!(!is_phantom_data(&ty));
175
176 let ty: Type = parse_quote! { Option<T> };
177 assert!(!is_phantom_data(&ty));
178 }
179
180 #[test]
181 fn test_is_phantom_data_reference() {
182 let ty: Type = parse_quote! { &PhantomData<T> };
184 assert!(!is_phantom_data(&ty));
185 }
186
187 #[test]
188 fn test_is_phantom_data_tuple() {
189 let ty: Type = parse_quote! { (PhantomData<T>,) };
191 assert!(!is_phantom_data(&ty));
192 }
193
194 #[test]
195 fn test_is_phantom_data_array() {
196 let ty: Type = parse_quote! { [PhantomData<T>; 1] };
198 assert!(!is_phantom_data(&ty));
199 }
200
201 #[test]
202 fn test_field_attr_cfg_default() {
203 let cfg = FieldAttrCfg::default();
205 assert!(!cfg.skip);
206 }
207
208 #[test]
209 fn test_parse_field_attrs_covers_line_27() {
210 let attrs: Vec<Attribute> = vec![parse_quote! { #[thread_aware(skip)] }, parse_quote! { #[thread_aware(skip)] }];
213 let result = parse_field_attrs(&attrs);
214 assert!(result.is_err());
215 let err_msg = result.unwrap_err().to_string();
216 assert!(err_msg.contains("duplicate"));
217 }
218}