// SPDX-License-Identifier: Apache-2.0 OR MIT12#[cfg(feature = "parsing")]3use crate::error::Error;4#[cfg(feature = "parsing")]5use crate::error::Result;6use crate::expr::Expr;7use crate::mac::MacroDelimiter;8#[cfg(feature = "parsing")]9use crate::meta::{self, ParseNestedMeta};10#[cfg(feature = "parsing")]11use crate::parse::{Parse, ParseStream, Parser};12use crate::path::Path;13use crate::token;14use proc_macro2::TokenStream;15#[cfg(feature = "printing")]16use std::iter;17#[cfg(feature = "printing")]18use std::slice;1920ast_struct! {21/// An attribute, like `#[repr(transparent)]`.22///23/// <br>24///25/// # Syntax26///27/// Rust has six types of attributes.28///29/// - Outer attributes like `#[repr(transparent)]`. These appear outside or30/// in front of the item they describe.31///32/// - Inner attributes like `#![feature(proc_macro)]`. These appear inside33/// of the item they describe, usually a module.34///35/// - Outer one-line doc comments like `/// Example`.36///37/// - Inner one-line doc comments like `//! Please file an issue`.38///39/// - Outer documentation blocks `/** Example */`.40///41/// - Inner documentation blocks `/*! Please file an issue */`.42///43/// The `style` field of type `AttrStyle` distinguishes whether an attribute44/// is outer or inner.45///46/// Every attribute has a `path` that indicates the intended interpretation47/// of the rest of the attribute's contents. The path and the optional48/// additional contents are represented together in the `meta` field of the49/// attribute in three possible varieties:50///51/// - Meta::Path — attributes whose information content conveys just a52/// path, for example the `#[test]` attribute.53///54/// - Meta::List — attributes that carry arbitrary tokens after the55/// path, surrounded by a delimiter (parenthesis, bracket, or brace). For56/// example `#[derive(Copy)]` or `#[precondition(x < 5)]`.57///58/// - Meta::NameValue — attributes with an `=` sign after the path,59/// followed by a Rust expression. For example `#[path =60/// "sys/windows.rs"]`.61///62/// All doc comments are represented in the NameValue style with a path of63/// "doc", as this is how they are processed by the compiler and by64/// `macro_rules!` macros.65///66/// ```text67/// #[derive(Copy, Clone)]68/// ~~~~~~Path69/// ^^^^^^^^^^^^^^^^^^^Meta::List70///71/// #[path = "sys/windows.rs"]72/// ~~~~Path73/// ^^^^^^^^^^^^^^^^^^^^^^^Meta::NameValue74///75/// #[test]76/// ^^^^Meta::Path77/// ```78///79/// <br>80///81/// # Parsing from tokens to Attribute82///83/// This type does not implement the [`Parse`] trait and thus cannot be84/// parsed directly by [`ParseStream::parse`]. Instead use85/// [`ParseStream::call`] with one of the two parser functions86/// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on87/// which you intend to parse.88///89/// [`Parse`]: crate::parse::Parse90/// [`ParseStream::parse`]: crate::parse::ParseBuffer::parse91/// [`ParseStream::call`]: crate::parse::ParseBuffer::call92///93/// ```94/// use syn::{Attribute, Ident, Result, Token};95/// use syn::parse::{Parse, ParseStream};96///97/// // Parses a unit struct with attributes.98/// //99/// // #[path = "s.tmpl"]100/// // struct S;101/// struct UnitStruct {102/// attrs: Vec<Attribute>,103/// struct_token: Token![struct],104/// name: Ident,105/// semi_token: Token![;],106/// }107///108/// impl Parse for UnitStruct {109/// fn parse(input: ParseStream) -> Result<Self> {110/// Ok(UnitStruct {111/// attrs: input.call(Attribute::parse_outer)?,112/// struct_token: input.parse()?,113/// name: input.parse()?,114/// semi_token: input.parse()?,115/// })116/// }117/// }118/// ```119///120/// <p><br></p>121///122/// # Parsing from Attribute to structured arguments123///124/// The grammar of attributes in Rust is very flexible, which makes the125/// syntax tree not that useful on its own. In particular, arguments of the126/// `Meta::List` variety of attribute are held in an arbitrary `tokens:127/// TokenStream`. Macros are expected to check the `path` of the attribute,128/// decide whether they recognize it, and then parse the remaining tokens129/// according to whatever grammar they wish to require for that kind of130/// attribute. Use [`parse_args()`] to parse those tokens into the expected131/// data structure.132///133/// [`parse_args()`]: Attribute::parse_args134///135/// <p><br></p>136///137/// # Doc comments138///139/// The compiler transforms doc comments, such as `/// comment` and `/*!140/// comment */`, into attributes before macros are expanded. Each comment is141/// expanded into an attribute of the form `#[doc = r"comment"]`.142///143/// As an example, the following `mod` items are expanded identically:144///145/// ```146/// # use syn::{ItemMod, parse_quote};147/// let doc: ItemMod = parse_quote! {148/// /// Single line doc comments149/// /// We write so many!150/// /**151/// * Multi-line comments...152/// * May span many lines153/// */154/// mod example {155/// //! Of course, they can be inner too156/// /*! And fit in a single line */157/// }158/// };159/// let attr: ItemMod = parse_quote! {160/// #[doc = r" Single line doc comments"]161/// #[doc = r" We write so many!"]162/// #[doc = r"163/// * Multi-line comments...164/// * May span many lines165/// "]166/// mod example {167/// #![doc = r" Of course, they can be inner too"]168/// #![doc = r" And fit in a single line "]169/// }170/// };171/// assert_eq!(doc, attr);172/// ```173#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]174pub struct Attribute {175pub pound_token: Token![#],176pub style: AttrStyle,177pub bracket_token: token::Bracket,178pub meta: Meta,179}180}181182impl Attribute {183/// Returns the path that identifies the interpretation of this attribute.184///185/// For example this would return the `test` in `#[test]`, the `derive` in186/// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.187pub fn path(&self) -> &Path {188self.meta.path()189}190191/// Parse the arguments to the attribute as a syntax tree.192///193/// This is similar to pulling out the `TokenStream` from `Meta::List` and194/// doing `syn::parse2::<T>(meta_list.tokens)`, except that using195/// `parse_args` the error message has a more useful span when `tokens` is196/// empty.197///198/// The surrounding delimiters are *not* included in the input to the199/// parser.200///201/// ```text202/// #[my_attr(value < 5)]203/// ^^^^^^^^^ what gets parsed204/// ```205///206/// # Example207///208/// ```209/// use syn::{parse_quote, Attribute, Expr};210///211/// let attr: Attribute = parse_quote! {212/// #[precondition(value < 5)]213/// };214///215/// if attr.path().is_ident("precondition") {216/// let precondition: Expr = attr.parse_args()?;217/// // ...218/// }219/// # anyhow::Ok(())220/// ```221#[cfg(feature = "parsing")]222#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]223pub fn parse_args<T: Parse>(&self) -> Result<T> {224self.parse_args_with(T::parse)225}226227/// Parse the arguments to the attribute using the given parser.228///229/// # Example230///231/// ```232/// use syn::{parse_quote, Attribute};233///234/// let attr: Attribute = parse_quote! {235/// #[inception { #[brrrrrrraaaaawwwwrwrrrmrmrmmrmrmmmmm] }]236/// };237///238/// let bwom = attr.parse_args_with(Attribute::parse_outer)?;239///240/// // Attribute does not have a Parse impl, so we couldn't directly do:241/// // let bwom: Attribute = attr.parse_args()?;242/// # anyhow::Ok(())243/// ```244#[cfg(feature = "parsing")]245#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]246pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {247match &self.meta {248Meta::Path(path) => Err(crate::error::new2(249path.segments.first().unwrap().ident.span(),250path.segments.last().unwrap().ident.span(),251format!(252"expected attribute arguments in parentheses: {}[{}(...)]",253parsing::DisplayAttrStyle(&self.style),254parsing::DisplayPath(path),255),256)),257Meta::NameValue(meta) => Err(Error::new(258meta.eq_token.span,259format_args!(260"expected parentheses: {}[{}(...)]",261parsing::DisplayAttrStyle(&self.style),262parsing::DisplayPath(&meta.path),263),264)),265Meta::List(meta) => meta.parse_args_with(parser),266}267}268269/// Parse the arguments to the attribute, expecting it to follow the270/// conventional structure used by most of Rust's built-in attributes.271///272/// The [*Meta Item Attribute Syntax*][syntax] section in the Rust reference273/// explains the convention in more detail. Not all attributes follow this274/// convention, so [`parse_args()`][Self::parse_args] is available if you275/// need to parse arbitrarily goofy attribute syntax.276///277/// [syntax]: https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax278///279/// # Example280///281/// We'll parse a struct, and then parse some of Rust's `#[repr]` attribute282/// syntax.283///284/// ```285/// use syn::{parenthesized, parse_quote, token, ItemStruct, LitInt};286///287/// let input: ItemStruct = parse_quote! {288/// #[repr(C, align(4))]289/// pub struct MyStruct(u16, u32);290/// };291///292/// let mut repr_c = false;293/// let mut repr_transparent = false;294/// let mut repr_align = None::<usize>;295/// let mut repr_packed = None::<usize>;296/// for attr in &input.attrs {297/// if attr.path().is_ident("repr") {298/// attr.parse_nested_meta(|meta| {299/// // #[repr(C)]300/// if meta.path.is_ident("C") {301/// repr_c = true;302/// return Ok(());303/// }304///305/// // #[repr(transparent)]306/// if meta.path.is_ident("transparent") {307/// repr_transparent = true;308/// return Ok(());309/// }310///311/// // #[repr(align(N))]312/// if meta.path.is_ident("align") {313/// let content;314/// parenthesized!(content in meta.input);315/// let lit: LitInt = content.parse()?;316/// let n: usize = lit.base10_parse()?;317/// repr_align = Some(n);318/// return Ok(());319/// }320///321/// // #[repr(packed)] or #[repr(packed(N))], omitted N means 1322/// if meta.path.is_ident("packed") {323/// if meta.input.peek(token::Paren) {324/// let content;325/// parenthesized!(content in meta.input);326/// let lit: LitInt = content.parse()?;327/// let n: usize = lit.base10_parse()?;328/// repr_packed = Some(n);329/// } else {330/// repr_packed = Some(1);331/// }332/// return Ok(());333/// }334///335/// Err(meta.error("unrecognized repr"))336/// })?;337/// }338/// }339/// # anyhow::Ok(())340/// ```341///342/// # Alternatives343///344/// In some cases, for attributes which have nested layers of structured345/// content, the following less flexible approach might be more convenient:346///347/// ```348/// # use syn::{parse_quote, ItemStruct};349/// #350/// # let input: ItemStruct = parse_quote! {351/// # #[repr(C, align(4))]352/// # pub struct MyStruct(u16, u32);353/// # };354/// #355/// use syn::punctuated::Punctuated;356/// use syn::{parenthesized, token, Error, LitInt, Meta, Token};357///358/// let mut repr_c = false;359/// let mut repr_transparent = false;360/// let mut repr_align = None::<usize>;361/// let mut repr_packed = None::<usize>;362/// for attr in &input.attrs {363/// if attr.path().is_ident("repr") {364/// let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;365/// for meta in nested {366/// match meta {367/// // #[repr(C)]368/// Meta::Path(path) if path.is_ident("C") => {369/// repr_c = true;370/// }371///372/// // #[repr(align(N))]373/// Meta::List(meta) if meta.path.is_ident("align") => {374/// let lit: LitInt = meta.parse_args()?;375/// let n: usize = lit.base10_parse()?;376/// repr_align = Some(n);377/// }378///379/// /* ... */380///381/// _ => {382/// return Err(Error::new_spanned(meta, "unrecognized repr"));383/// }384/// }385/// }386/// }387/// }388/// # Ok(())389/// ```390#[cfg(feature = "parsing")]391#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]392pub fn parse_nested_meta(393&self,394logic: impl FnMut(ParseNestedMeta) -> Result<()>,395) -> Result<()> {396self.parse_args_with(meta::parser(logic))397}398399/// Parses zero or more outer attributes from the stream.400///401/// # Example402///403/// See404/// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute).405#[cfg(feature = "parsing")]406#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]407pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> {408let mut attrs = Vec::new();409while input.peek(Token![#]) {410attrs.push(input.call(parsing::single_parse_outer)?);411}412Ok(attrs)413}414415/// Parses zero or more inner attributes from the stream.416///417/// # Example418///419/// See420/// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute).421#[cfg(feature = "parsing")]422#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]423pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> {424let mut attrs = Vec::new();425parsing::parse_inner(input, &mut attrs)?;426Ok(attrs)427}428}429430ast_enum! {431/// Distinguishes between attributes that decorate an item and attributes432/// that are contained within an item.433///434/// # Outer attributes435///436/// - `#[repr(transparent)]`437/// - `/// # Example`438/// - `/** Please file an issue */`439///440/// # Inner attributes441///442/// - `#![feature(proc_macro)]`443/// - `//! # Example`444/// - `/*! Please file an issue */`445#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]446pub enum AttrStyle {447Outer,448Inner(Token![!]),449}450}451452ast_enum! {453/// Content of a compile-time structured attribute.454///455/// ## Path456///457/// A meta path is like the `test` in `#[test]`.458///459/// ## List460///461/// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.462///463/// ## NameValue464///465/// A name-value meta is like the `path = "..."` in `#[path =466/// "sys/windows.rs"]`.467///468/// # Syntax tree enum469///470/// This type is a [syntax tree enum].471///472/// [syntax tree enum]: crate::expr::Expr#syntax-tree-enums473#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]474pub enum Meta {475Path(Path),476477/// A structured list within an attribute, like `derive(Copy, Clone)`.478List(MetaList),479480/// A name-value pair within an attribute, like `feature = "nightly"`.481NameValue(MetaNameValue),482}483}484485ast_struct! {486/// A structured list within an attribute, like `derive(Copy, Clone)`.487#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]488pub struct MetaList {489pub path: Path,490pub delimiter: MacroDelimiter,491pub tokens: TokenStream,492}493}494495ast_struct! {496/// A name-value pair within an attribute, like `feature = "nightly"`.497#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]498pub struct MetaNameValue {499pub path: Path,500pub eq_token: Token![=],501pub value: Expr,502}503}504505impl Meta {506/// Returns the path that begins this structured meta item.507///508/// For example this would return the `test` in `#[test]`, the `derive` in509/// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.510pub fn path(&self) -> &Path {511match self {512Meta::Path(path) => path,513Meta::List(meta) => &meta.path,514Meta::NameValue(meta) => &meta.path,515}516}517518/// Error if this is a `Meta::List` or `Meta::NameValue`.519#[cfg(feature = "parsing")]520#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]521pub fn require_path_only(&self) -> Result<&Path> {522let error_span = match self {523Meta::Path(path) => return Ok(path),524Meta::List(meta) => meta.delimiter.span().open(),525Meta::NameValue(meta) => meta.eq_token.span,526};527Err(Error::new(error_span, "unexpected token in attribute"))528}529530/// Error if this is a `Meta::Path` or `Meta::NameValue`.531#[cfg(feature = "parsing")]532#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]533pub fn require_list(&self) -> Result<&MetaList> {534match self {535Meta::List(meta) => Ok(meta),536Meta::Path(path) => Err(crate::error::new2(537path.segments.first().unwrap().ident.span(),538path.segments.last().unwrap().ident.span(),539format!(540"expected attribute arguments in parentheses: `{}(...)`",541parsing::DisplayPath(path),542),543)),544Meta::NameValue(meta) => Err(Error::new(meta.eq_token.span, "expected `(`")),545}546}547548/// Error if this is a `Meta::Path` or `Meta::List`.549#[cfg(feature = "parsing")]550#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]551pub fn require_name_value(&self) -> Result<&MetaNameValue> {552match self {553Meta::NameValue(meta) => Ok(meta),554Meta::Path(path) => Err(crate::error::new2(555path.segments.first().unwrap().ident.span(),556path.segments.last().unwrap().ident.span(),557format!(558"expected a value for this attribute: `{} = ...`",559parsing::DisplayPath(path),560),561)),562Meta::List(meta) => Err(Error::new(meta.delimiter.span().open(), "expected `=`")),563}564}565}566567impl MetaList {568/// See [`Attribute::parse_args`].569#[cfg(feature = "parsing")]570#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]571pub fn parse_args<T: Parse>(&self) -> Result<T> {572self.parse_args_with(T::parse)573}574575/// See [`Attribute::parse_args_with`].576#[cfg(feature = "parsing")]577#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]578pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {579let scope = self.delimiter.span().close();580crate::parse::parse_scoped(parser, scope, self.tokens.clone())581}582583/// See [`Attribute::parse_nested_meta`].584#[cfg(feature = "parsing")]585#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]586pub fn parse_nested_meta(587&self,588logic: impl FnMut(ParseNestedMeta) -> Result<()>,589) -> Result<()> {590self.parse_args_with(meta::parser(logic))591}592}593594#[cfg(feature = "printing")]595pub(crate) trait FilterAttrs<'a> {596type Ret: Iterator<Item = &'a Attribute>;597598fn outer(self) -> Self::Ret;599#[cfg(feature = "full")]600fn inner(self) -> Self::Ret;601}602603#[cfg(feature = "printing")]604impl<'a> FilterAttrs<'a> for &'a [Attribute] {605type Ret = iter::Filter<slice::Iter<'a, Attribute>, fn(&&Attribute) -> bool>;606607fn outer(self) -> Self::Ret {608fn is_outer(attr: &&Attribute) -> bool {609match attr.style {610AttrStyle::Outer => true,611AttrStyle::Inner(_) => false,612}613}614self.iter().filter(is_outer)615}616617#[cfg(feature = "full")]618fn inner(self) -> Self::Ret {619fn is_inner(attr: &&Attribute) -> bool {620match attr.style {621AttrStyle::Inner(_) => true,622AttrStyle::Outer => false,623}624}625self.iter().filter(is_inner)626}627}628629impl From<Path> for Meta {630fn from(meta: Path) -> Meta {631Meta::Path(meta)632}633}634635impl From<MetaList> for Meta {636fn from(meta: MetaList) -> Meta {637Meta::List(meta)638}639}640641impl From<MetaNameValue> for Meta {642fn from(meta: MetaNameValue) -> Meta {643Meta::NameValue(meta)644}645}646647#[cfg(feature = "parsing")]648pub(crate) mod parsing {649use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue};650use crate::error::Result;651use crate::expr::{Expr, ExprLit};652use crate::lit::Lit;653use crate::parse::discouraged::Speculative as _;654use crate::parse::{Parse, ParseStream};655use crate::path::Path;656use crate::{mac, token};657use proc_macro2::Ident;658use std::fmt::{self, Display};659660pub(crate) fn parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()> {661while input.peek(Token![#]) && input.peek2(Token![!]) {662attrs.push(input.call(single_parse_inner)?);663}664Ok(())665}666667pub(crate) fn single_parse_inner(input: ParseStream) -> Result<Attribute> {668let content;669Ok(Attribute {670pound_token: input.parse()?,671style: AttrStyle::Inner(input.parse()?),672bracket_token: bracketed!(content in input),673meta: content.parse()?,674})675}676677pub(crate) fn single_parse_outer(input: ParseStream) -> Result<Attribute> {678let content;679Ok(Attribute {680pound_token: input.parse()?,681style: AttrStyle::Outer,682bracket_token: bracketed!(content in input),683meta: content.parse()?,684})685}686687#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]688impl Parse for Meta {689fn parse(input: ParseStream) -> Result<Self> {690let path = parse_outermost_meta_path(input)?;691parse_meta_after_path(path, input)692}693}694695#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]696impl Parse for MetaList {697fn parse(input: ParseStream) -> Result<Self> {698let path = parse_outermost_meta_path(input)?;699parse_meta_list_after_path(path, input)700}701}702703#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]704impl Parse for MetaNameValue {705fn parse(input: ParseStream) -> Result<Self> {706let path = parse_outermost_meta_path(input)?;707parse_meta_name_value_after_path(path, input)708}709}710711// Unlike meta::parse_meta_path which accepts arbitrary keywords in the path,712// only the `unsafe` keyword is accepted as an attribute's outermost path.713fn parse_outermost_meta_path(input: ParseStream) -> Result<Path> {714if input.peek(Token![unsafe]) {715let unsafe_token: Token![unsafe] = input.parse()?;716Ok(Path::from(Ident::new("unsafe", unsafe_token.span)))717} else {718Path::parse_mod_style(input)719}720}721722pub(crate) fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> {723if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) {724parse_meta_list_after_path(path, input).map(Meta::List)725} else if input.peek(Token![=]) {726parse_meta_name_value_after_path(path, input).map(Meta::NameValue)727} else {728Ok(Meta::Path(path))729}730}731732fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> {733let (delimiter, tokens) = mac::parse_delimiter(input)?;734Ok(MetaList {735path,736delimiter,737tokens,738})739}740741fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> {742let eq_token: Token![=] = input.parse()?;743let ahead = input.fork();744let lit: Option<Lit> = ahead.parse()?;745let value = if let (Some(lit), true) = (lit, ahead.is_empty()) {746input.advance_to(&ahead);747Expr::Lit(ExprLit {748attrs: Vec::new(),749lit,750})751} else if input.peek(Token![#]) && input.peek2(token::Bracket) {752return Err(input.error("unexpected attribute inside of attribute"));753} else {754input.parse()?755};756Ok(MetaNameValue {757path,758eq_token,759value,760})761}762763pub(super) struct DisplayAttrStyle<'a>(pub &'a AttrStyle);764765impl<'a> Display for DisplayAttrStyle<'a> {766fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {767formatter.write_str(match self.0 {768AttrStyle::Outer => "#",769AttrStyle::Inner(_) => "#!",770})771}772}773774pub(super) struct DisplayPath<'a>(pub &'a Path);775776impl<'a> Display for DisplayPath<'a> {777fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {778for (i, segment) in self.0.segments.iter().enumerate() {779if i > 0 || self.0.leading_colon.is_some() {780formatter.write_str("::")?;781}782write!(formatter, "{}", segment.ident)?;783}784Ok(())785}786}787}788789#[cfg(feature = "printing")]790mod printing {791use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue};792use crate::path;793use crate::path::printing::PathStyle;794use proc_macro2::TokenStream;795use quote::ToTokens;796797#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]798impl ToTokens for Attribute {799fn to_tokens(&self, tokens: &mut TokenStream) {800self.pound_token.to_tokens(tokens);801if let AttrStyle::Inner(b) = &self.style {802b.to_tokens(tokens);803}804self.bracket_token.surround(tokens, |tokens| {805self.meta.to_tokens(tokens);806});807}808}809810#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]811impl ToTokens for Meta {812fn to_tokens(&self, tokens: &mut TokenStream) {813match self {814Meta::Path(path) => path::printing::print_path(tokens, path, PathStyle::Mod),815Meta::List(meta_list) => meta_list.to_tokens(tokens),816Meta::NameValue(meta_name_value) => meta_name_value.to_tokens(tokens),817}818}819}820821#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]822impl ToTokens for MetaList {823fn to_tokens(&self, tokens: &mut TokenStream) {824path::printing::print_path(tokens, &self.path, PathStyle::Mod);825self.delimiter.surround(tokens, self.tokens.clone());826}827}828829#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]830impl ToTokens for MetaNameValue {831fn to_tokens(&self, tokens: &mut TokenStream) {832path::printing::print_path(tokens, &self.path, PathStyle::Mod);833self.eq_token.to_tokens(tokens);834self.value.to_tokens(tokens);835}836}837}838839840