macro_tools/
attr.rs

1//!
2//! Attributes analyzys and manipulation.
3//!
4
5/// Define a private namespace for all its items.
6mod private
7{
8  #[ allow( clippy::wildcard_imports ) ]
9  use crate::*;
10
11  /// Checks if the given iterator of attributes contains an attribute named `debug`.
12  ///
13  /// This function iterates over an input sequence of `syn::Attribute`, typically associated with a struct,
14  /// enum, or other item in a Rust Abstract Syntax Tree ( AST ), and determines whether any of the attributes
15  /// is exactly named `debug`.
16  ///
17  /// # Parameters
18  /// - `attrs` : An iterator over `syn::Attribute`. This could be obtained from parsing Rust code
19  ///   with the `syn` crate, where the iterator represents attributes applied to a Rust item ( like a struct or function ).
20  ///
21  /// # Returns
22  /// - `Ok( true )` if the `debug` attribute is present.
23  /// - `Ok( false )` if the `debug` attribute is not found.
24  /// - `Err( syn::Error )` if an unknown or improperly formatted attribute is encountered.
25  ///
26  /// # Example
27  ///
28  /// Suppose you have the following struct definition in a procedural macro input:
29  ///
30  /// ```rust, ignore
31  /// #[ derive( SomeDerive ) ]
32  /// #[ debug ]
33  /// struct MyStruct
34  /// {
35  ///   field : i32,
36  /// }
37  /// ```
38  ///
39  /// You can use `has_debug` to check for the presence of the `debug` attribute:
40  ///
41  /// ```rust
42  /// use macro_tools::exposed::*;
43  ///
44  /// // Example struct attribute
45  /// let attrs : Vec< syn::Attribute > = vec![ syn::parse_quote!( #[ debug ] ) ];
46  ///
47  /// // Checking for 'debug' attribute
48  /// let contains_debug = attr::has_debug( ( &attrs ).into_iter() ).unwrap();
49  ///
50  /// assert!( contains_debug, "Expected to find 'debug' attribute" );
51  /// ```
52  /// # Errors
53  /// qqq: doc
54  pub fn has_debug< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> syn::Result< bool >
55  {
56    for attr in attrs
57    {
58      if let Some( ident ) = attr.path().get_ident()
59      {
60        let ident_string = format!( "{ident}" );
61        if ident_string == "debug"
62        {
63          return Ok( true )
64        }
65      }
66      else
67      {
68        return_syn_err!( "Unknown structure attribute:\n{}", qt!{ attr } );
69      }
70    }
71    Ok( false )
72  }
73
74  /// Checks if the given attribute name is a standard Rust attribute.
75  ///
76  /// Standard Rust attributes are those which are recognized and processed
77  /// directly by the Rust compiler. They influence various aspects of compilation,
78  /// including but not limited to conditional compilation, optimization hints,
79  /// code visibility, and procedural macro behavior.
80  ///
81  /// This function is useful when developing tools that need to interact with or
82  /// understand the significance of specific attributes in Rust source code, such
83  /// as linters, code analyzers, or procedural macros.
84  ///
85  /// This function does not cover all possible attributes but includes many of the
86  /// common ones that are relevant to most Rust projects. Developers are encouraged
87  /// to update this function as needed to suit more specialized needs, especially
88  /// when dealing with nightly-only compiler attributes or deprecated ones.
89  ///
90  /// # Parameters
91  /// - `attr_name`: A string slice that holds the name of the attribute to check.
92  ///
93  /// # Returns
94  /// Returns `true` if `attr_name` is a recognized standard Rust attribute. Otherwise,
95  /// returns `false`.
96  ///
97  /// # Examples
98  ///
99  /// Standard attributes:
100  ///
101  /// ```
102  /// assert_eq!( macro_tools::attr::is_standard( "cfg" ), true );
103  /// assert_eq!( macro_tools::attr::is_standard( "inline" ), true );
104  /// assert_eq!( macro_tools::attr::is_standard( "derive" ), true );
105  /// ```
106  ///
107  /// Non-standard or custom attributes:
108  ///
109  /// ```
110  /// assert_eq!( macro_tools::attr::is_standard( "custom_attr" ), false );
111  /// assert_eq!( macro_tools::attr::is_standard( "my_attribute" ), false );
112  /// ```
113  ///
114  #[ must_use ]
115  #[ allow( clippy::match_same_arms ) ]
116  pub fn is_standard( attr_name : &str ) -> bool
117  {
118    match attr_name
119    {
120      // Conditional compilation
121      "cfg" | "cfg_attr" => true,
122
123      // Compiler instructions and optimizations
124      "inline" | "repr" | "derive" | "allow" | "warn" | "deny" | "forbid" => true,
125
126      // Testing attributes
127      "test" | "bench" => true,
128
129      // Documentation attributes
130      "doc" => true,
131
132      // Visibility and accessibility
133      "pub" => true, // This would typically need context to be accurate
134
135      // Safety and ABI
136      "unsafe" | "no_mangle" | "extern" => true,
137
138      // Module and Crate configuration
139      "path" | "macro_use" | "crate_type" | "crate_name" => true,
140
141      // Linking
142      "link" | "link_name" | "link_section" => true,
143
144      // Usage warnings
145      "must_use" => true,
146
147      // Other attributes
148      "cold" | "export_name" | "global_allocator" => true,
149
150      // Module handling
151      "used" | "unused" => true,
152
153      // Procedural macros and hygiene
154      "proc_macro" | "proc_macro_derive" | "proc_macro_attribute" => true,
155
156      // Stability attributes
157      "stable" | "unstable" | "rustc_const_unstable" | "rustc_const_stable" |
158      "rustc_diagnostic_item" | "rustc_deprecated" | "rustc_legacy_const_generics" => true,
159
160      // Special compiler attributes
161      "feature" | "non_exhaustive" => true,
162
163      // Future compatibility
164      "rustc_paren_sugar" | "rustc_insignificant_dtor" => true,
165
166      // Type system extensions
167      "opaque" => true,
168
169      // Miscellaneous
170      "track_caller" => true,
171
172      // Default case
173      _ => false,
174    }
175  }
176
177  ///
178  /// Attribute which is inner.
179  ///
180  /// For example: `// #![ deny( missing_docs ) ]`.
181  ///
182
183  #[ derive( Debug, PartialEq, Eq, Clone, Default ) ]
184  pub struct AttributesInner( pub Vec< syn::Attribute > );
185
186  impl From< Vec< syn::Attribute > > for AttributesInner
187  {
188    #[ inline( always ) ]
189    fn from( src : Vec< syn::Attribute > ) -> Self
190    {
191      Self( src )
192    }
193  }
194
195  impl From< AttributesInner > for Vec< syn::Attribute >
196  {
197    #[ inline( always ) ]
198    fn from( src : AttributesInner ) -> Self
199    {
200      src.0
201    }
202  }
203
204  #[ allow( clippy::iter_without_into_iter ) ]
205  impl AttributesInner
206  {
207    /// Iterator
208    pub fn iter( &self ) -> core::slice::Iter< '_, syn::Attribute >
209    {
210      self.0.iter()
211    }
212  }
213
214  #[ allow( clippy::default_trait_access ) ]
215  impl syn::parse::Parse
216  for AttributesInner
217  {
218    fn parse( input : ParseStream< '_ > ) -> syn::Result< Self >
219    {
220      // let mut result : Self = from!();
221      let mut result : Self = Default::default();
222      loop
223      {
224        if !input.peek( Token![ # ] ) || !input.peek2( Token![ ! ] )
225        {
226          break;
227        }
228        let input2;
229        let element = syn::Attribute
230        {
231          pound_token : input.parse()?,
232          style : syn::AttrStyle::Inner( input.parse()? ),
233          bracket_token : bracketed!( input2 in input ),
234          // path : input2.call( syn::Path::parse_mod_style )?,
235          // tokens : input2.parse()?,
236          meta : input2.parse()?,
237        };
238        result.0.push( element );
239      }
240      Ok( result )
241    }
242  }
243
244  impl quote::ToTokens
245  for AttributesInner
246  {
247    fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream )
248    {
249      use crate::quote::TokenStreamExt;
250      tokens.append_all( self.0.iter() );
251    }
252  }
253
254  /// Represents a collection of outer attributes.
255  ///
256  /// This struct wraps a `Vec< syn::Attribute >`, providing utility methods for parsing,
257  /// converting, and iterating over outer attributes. Outer attributes are those that
258  /// appear outside of an item, such as `#[ ... ]` annotations in Rust.
259  ///
260  #[ derive( Debug, PartialEq, Eq, Clone, Default ) ]
261  pub struct AttributesOuter( pub Vec< syn::Attribute > );
262
263  impl From< Vec< syn::Attribute > > for AttributesOuter
264  {
265    #[ inline( always ) ]
266    fn from( src : Vec< syn::Attribute > ) -> Self
267    {
268      Self( src )
269    }
270  }
271
272  impl From< AttributesOuter > for Vec< syn::Attribute >
273  {
274    #[ inline( always ) ]
275    fn from( src : AttributesOuter ) -> Self
276    {
277      src.0
278    }
279  }
280
281  #[ allow( clippy::iter_without_into_iter ) ]
282  impl AttributesOuter
283  {
284    /// Iterator
285    pub fn iter( &self ) -> core::slice::Iter< '_, syn::Attribute >
286    {
287      self.0.iter()
288    }
289  }
290
291  #[ allow( clippy::default_trait_access ) ]
292  impl syn::parse::Parse
293  for AttributesOuter
294  {
295    fn parse( input : ParseStream< '_ > ) -> syn::Result< Self >
296    {
297      let mut result : Self = Default::default();
298      loop
299      {
300        if !input.peek( Token![ # ] ) || input.peek2( Token![ ! ] )
301        {
302          break;
303        }
304        let input2;
305        let element = syn::Attribute
306        {
307          pound_token : input.parse()?,
308          style : syn::AttrStyle::Outer,
309          bracket_token : bracketed!( input2 in input ),
310          // path : input2.call( syn::Path::parse_mod_style )?,
311          // tokens : input2.parse()?,
312          meta : input2.parse()?,
313        };
314        result.0.push( element );
315      }
316      Ok( result )
317    }
318  }
319
320  impl quote::ToTokens
321  for AttributesOuter
322  {
323    fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream )
324    {
325      use crate::quote::TokenStreamExt;
326      tokens.append_all( self.0.iter() );
327    }
328  }
329
330  impl syn::parse::Parse
331  for Many< AttributesInner >
332  {
333    fn parse( input : ParseStream< '_ > ) -> syn::Result< Self >
334    {
335      let mut result = Self::new();
336      loop
337      {
338        // let lookahead = input.lookahead1();
339        if !input.peek( Token![ # ] )
340        {
341          break;
342        }
343        result.0.push( input.parse()? );
344      }
345      Ok( result )
346    }
347  }
348
349  impl syn::parse::Parse
350  for Many< AttributesOuter >
351  {
352    fn parse( input : ParseStream< '_ > ) -> syn::Result< Self >
353    {
354      let mut result = Self::new();
355      loop
356      {
357        // let lookahead = input.lookahead1();
358        if !input.peek( Token![ # ] )
359        {
360          break;
361        }
362        result.0.push( input.parse()? );
363      }
364      Ok( result )
365    }
366  }
367
368  impl AsMuchAsPossibleNoDelimiter for syn::Item {}
369
370  /// Trait for components of a structure aggregating attributes that can be constructed from a meta attribute.
371  ///
372  /// The `AttributeComponent` trait defines the interface for components that can be created
373  /// from a `syn::Attribute` meta item. Implementors of this trait are required to define
374  /// a constant `KEYWORD` that identifies the type of the component and a method `from_meta`
375  /// that handles the construction of the component from the given attribute.
376  ///
377  /// This trait is designed to facilitate modular and reusable parsing of attributes applied
378  /// to structs, enums, or other constructs. By implementing this trait, you can create specific
379  /// components from attributes and then aggregate these components into a larger structure.
380  ///
381  /// # Example
382  ///
383  /// ```rust
384  /// use macro_tools::{ AttributeComponent, syn::Result };
385  /// use syn::{ Attribute, Error };
386  ///
387  /// struct MyComponent;
388  ///
389  /// impl AttributeComponent for MyComponent
390  /// {
391  ///   const KEYWORD : &'static str = "my_component";
392  ///
393  ///   fn from_meta( attr : &Attribute ) -> syn::Result<Self>
394  ///   {
395  ///     // Parsing logic here
396  ///     // Return Ok(MyComponent) if parsing is successful
397  ///     // Return Err(Error::new_spanned(attr, "error message")) if parsing fails
398  ///     Ok( MyComponent )
399  ///   }
400  /// }
401  /// ```
402  ///
403  /// # Parameters
404  ///
405  /// - `attr` : A reference to the `syn::Attribute` from which the component is to be constructed.
406  ///
407  /// # Returns
408  ///
409  /// A `syn::Result` containing the constructed component if successful, or an error if the parsing fails.
410  ///
411  pub trait AttributeComponent
412  where
413    Self : Sized,
414  {
415    /// The keyword that identifies the component.
416    ///
417    /// This constant is used to match the attribute to the corresponding component.
418    /// Each implementor of this trait must provide a unique keyword for its type.
419    const KEYWORD : &'static str;
420
421    /// Constructs the component from the given meta attribute.
422    ///
423    /// This method is responsible for parsing the provided `syn::Attribute` and
424    /// returning an instance of the component. If the attribute cannot be parsed
425    /// into the component, an error should be returned.
426    ///
427    /// # Parameters
428    ///
429    /// - `attr` : A reference to the `syn::Attribute` from which the component is to be constructed.
430    ///
431    /// # Returns
432    ///
433    /// A `syn::Result` containing the constructed component if successful, or an error if the parsing fails.
434    /// 
435    /// # Errors
436    /// qqq: doc
437    fn from_meta( attr : &syn::Attribute ) -> syn::Result< Self >;
438
439    // zzz : redo maybe
440  }
441
442}
443
444#[ doc( inline ) ]
445#[ allow( unused_imports ) ]
446pub use own::*;
447
448/// Own namespace of the module.
449#[ allow( unused_imports ) ]
450pub mod own
451{
452  #[ allow( clippy::wildcard_imports ) ]
453  use super::*;
454  #[ doc( inline ) ]
455  pub use orphan::*;
456  #[ doc( inline ) ]
457  pub use private::
458  {
459    // equation,
460    has_debug,
461    is_standard,
462  };
463}
464
465/// Orphan namespace of the module.
466#[ allow( unused_imports ) ]
467pub mod orphan
468{
469  #[ allow( clippy::wildcard_imports ) ]
470  use super::*;
471  #[ doc( inline ) ]
472  pub use exposed::*;
473}
474
475/// Exposed namespace of the module.
476#[ allow( unused_imports ) ]
477pub mod exposed
478{
479  #[ allow( clippy::wildcard_imports ) ]
480  use super::*;
481  pub use super::super::attr;
482
483  #[ doc( inline ) ]
484  pub use prelude::*;
485  #[ doc( inline ) ]
486  pub use private::
487  {
488    AttributesInner,
489    AttributesOuter,
490    AttributeComponent,
491  };
492}
493
494/// Prelude to use essentials: `use my_module::prelude::*`.
495#[ allow( unused_imports ) ]
496pub mod prelude
497{
498  use super::*;
499}