@@ -22,7 +22,7 @@ use std::path::PathBuf;
2222#[ cfg( feature = "rustc-hash" ) ]
2323use rustc_hash:: FxHashMap as HashMap ;
2424use serde_derive:: { Deserialize , Serialize } ;
25-
25+ use serde :: de :: Error ;
2626
2727/// The version of JSON output that this crate represents.
2828///
@@ -201,7 +201,7 @@ pub struct Item {
201201 pub inner : ItemEnum ,
202202}
203203
204- #[ derive( Clone , Debug , PartialEq , Eq , Serialize , Deserialize ) ]
204+ #[ derive( Clone , Debug , PartialEq , Eq , Serialize ) ]
205205#[ serde( rename_all = "snake_case" ) ]
206206/// An attribute, e.g. `#[repr(C)]`
207207///
@@ -242,11 +242,108 @@ pub enum Attribute {
242242 /// 1. A HIR debug printing, like `"#[attr = Optimize(Speed)]"`
243243 /// 2. The attribute as it appears in source form, like
244244 /// `"#[optimize(speed)]"`.
245- // XXX: This variant must be last in the enum because it is untagged.
246- #[ serde( untagged) ]
247245 Other ( String ) ,
248246}
249247
248+ impl < ' de > serde:: Deserialize < ' de > for Attribute {
249+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
250+ where
251+ D : serde:: Deserializer < ' de > ,
252+ {
253+ let s = String :: deserialize ( deserializer) ?;
254+
255+ // Strip #[ and ] if present
256+ let cleaned = s. trim_start_matches ( "#[" )
257+ . trim_end_matches ( "]" )
258+ . trim ( ) ;
259+
260+ // Now match on the cleaned string
261+ Ok ( match cleaned {
262+ "non_exhaustive" => Attribute :: NonExhaustive ,
263+ s if s. starts_with ( "must_use" ) => {
264+ // Parse reason if present
265+ let reason = if s. len ( ) > "must_use" . len ( ) {
266+ Some ( s[ "must_use" . len ( ) ..] . trim_matches ( |c| c == '(' || c == ')' || c == '"' ) . to_string ( ) )
267+ } else {
268+ None
269+ } ;
270+ Attribute :: MustUse { reason }
271+ } ,
272+ s if s. starts_with ( "export_name" ) => {
273+ let name = s[ "export_name" . len ( ) ..] . trim_matches ( |c| c == '=' || c == ' ' || c == '"' ) . to_string ( ) ;
274+ Attribute :: ExportName ( name)
275+ } ,
276+
277+ s if s. starts_with ( "export_name" ) => {
278+ let name = s[ "export_name" . len ( ) ..] . trim_matches ( |c| c == '=' || c == ' ' || c == '"' ) . to_string ( ) ;
279+ Attribute :: ExportName ( name)
280+ } ,
281+
282+ s if s. starts_with ( "link_section" ) => {
283+ let section = s[ "link_section" . len ( ) ..] . trim_matches ( |c| c == '=' || c == ' ' || c == '"' ) . to_string ( ) ;
284+ Attribute :: LinkSection ( section)
285+ } ,
286+
287+ "automatically_derived" => Attribute :: AutomaticallyDerived ,
288+
289+ s if s. starts_with ( "repr" ) => {
290+ let repr_str = s[ "repr" . len ( ) ..] . trim_matches ( |c| c == '(' || c == ')' ) . trim ( ) ;
291+ let parts: Vec < & str > = repr_str. split ( ',' ) . map ( str:: trim) . collect ( ) ;
292+
293+ let mut repr = AttributeRepr {
294+ kind : ReprKind :: C , // Will be overwritten
295+ align : None ,
296+ packed : None ,
297+ int : None ,
298+ } ;
299+
300+ for part in parts {
301+ if part. starts_with ( "align(" ) {
302+ let align_str = part. trim_start_matches ( "align(" ) . trim_end_matches ( ')' ) ;
303+ repr. align = Some ( align_str. parse ( ) . map_err ( D :: Error :: custom) ?) ;
304+ } else if part. starts_with ( "packed(" ) {
305+ let packed_str = part. trim_start_matches ( "packed(" ) . trim_end_matches ( ')' ) ;
306+ repr. packed = Some ( packed_str. parse ( ) . map_err ( D :: Error :: custom) ?) ;
307+ } else if part == "C" {
308+ repr. kind = ReprKind :: C ;
309+ } else if part == "transparent" {
310+ repr. kind = ReprKind :: Transparent ;
311+ } else if [ "i8" , "i16" , "i32" , "i64" , "i128" , "isize" ,
312+ "u8" , "u16" , "u32" , "u64" , "u128" , "usize" ] . contains ( & part) {
313+ repr. int = Some ( part. to_string ( ) ) ;
314+ }
315+ // Add other ReprKind variants as needed
316+ }
317+
318+ Attribute :: Repr ( repr)
319+ } ,
320+
321+ "no_mangle" => Attribute :: NoMangle ,
322+
323+ s if s. starts_with ( "target_feature" ) => {
324+ let features_str = s[ "target_feature" . len ( ) ..] . trim_matches ( |c| c == '(' || c == ')' ) . trim ( ) ;
325+ let enable: Vec < String > = features_str
326+ . split ( ',' )
327+ . filter_map ( |feature| {
328+ let feature = feature. trim ( ) ;
329+ if feature. starts_with ( "enable = " ) {
330+ Some ( feature[ "enable = " . len ( ) ..] . trim_matches ( '"' ) . to_string ( ) )
331+ } else {
332+ None
333+ }
334+ } )
335+ . collect ( ) ;
336+ Attribute :: TargetFeature { enable }
337+ } ,
338+
339+
340+ // Default case
341+ _ => Attribute :: Other ( s. to_string ( ) ) ,
342+ } )
343+ }
344+ }
345+
346+
250347#[ derive( Clone , Debug , PartialEq , Eq , Serialize , Deserialize ) ]
251348/// The contents of a `#[repr(...)]` attribute.
252349///
0 commit comments