diff --git a/src/attr.rs b/src/attr.rs index c19715cb3b..579452bbf7 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -448,7 +448,7 @@ ast_enum! { } } -ast_enum_of_structs! { +ast_enum! { /// Content of a compile-time structured attribute. /// /// ## Path @@ -625,6 +625,24 @@ impl<'a> FilterAttrs<'a> for &'a [Attribute] { } } +impl From for Meta { + fn from(meta: Path) -> Meta { + Meta::Path(meta) + } +} + +impl From for Meta { + fn from(meta: MetaList) -> Meta { + Meta::List(meta) + } +} + +impl From for Meta { + fn from(meta: MetaNameValue) -> Meta { + Meta::NameValue(meta) + } +} + #[cfg(feature = "parsing")] pub(crate) mod parsing { use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue}; @@ -757,7 +775,9 @@ pub(crate) mod parsing { #[cfg(feature = "printing")] mod printing { - use crate::attr::{AttrStyle, Attribute, MetaList, MetaNameValue}; + use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue}; + use crate::path; + use crate::path::printing::PathStyle; use proc_macro2::TokenStream; use quote::ToTokens; @@ -774,10 +794,21 @@ mod printing { } } + #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] + impl ToTokens for Meta { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Meta::Path(path) => path::printing::print_path(tokens, path, PathStyle::Mod), + Meta::List(meta_list) => meta_list.to_tokens(tokens), + Meta::NameValue(meta_name_value) => meta_name_value.to_tokens(tokens), + } + } + } + #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for MetaList { fn to_tokens(&self, tokens: &mut TokenStream) { - self.path.to_tokens(tokens); + path::printing::print_path(tokens, &self.path, PathStyle::Mod); self.delimiter.surround(tokens, self.tokens.clone()); } } @@ -785,7 +816,7 @@ mod printing { #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for MetaNameValue { fn to_tokens(&self, tokens: &mut TokenStream) { - self.path.to_tokens(tokens); + path::printing::print_path(tokens, &self.path, PathStyle::Mod); self.eq_token.to_tokens(tokens); self.value.to_tokens(tokens); } diff --git a/src/expr.rs b/src/expr.rs index e7b462c0ee..1d7c7bf4b5 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3013,6 +3013,7 @@ pub(crate) mod printing { use crate::fixup::FixupContext; use crate::op::BinOp; use crate::path; + use crate::path::printing::PathStyle; use crate::precedence::Precedence; use crate::token; #[cfg(feature = "full")] @@ -3626,7 +3627,13 @@ pub(crate) mod printing { ); e.dot_token.to_tokens(tokens); e.method.to_tokens(tokens); - e.turbofish.to_tokens(tokens); + if let Some(turbofish) = &e.turbofish { + path::printing::print_angle_bracketed_generic_arguments( + tokens, + turbofish, + PathStyle::Expr, + ); + } e.paren_token.surround(tokens, |tokens| { e.args.to_tokens(tokens); }); @@ -3646,7 +3653,7 @@ pub(crate) mod printing { impl ToTokens for ExprPath { fn to_tokens(&self, tokens: &mut TokenStream) { outer_attrs_to_tokens(&self.attrs, tokens); - path::printing::print_path(tokens, &self.qself, &self.path); + path::printing::print_qpath(tokens, &self.qself, &self.path, PathStyle::Expr); } } @@ -3733,7 +3740,7 @@ pub(crate) mod printing { impl ToTokens for ExprStruct { fn to_tokens(&self, tokens: &mut TokenStream) { outer_attrs_to_tokens(&self.attrs, tokens); - path::printing::print_path(tokens, &self.qself, &self.path); + path::printing::print_qpath(tokens, &self.qself, &self.path, PathStyle::Expr); self.brace_token.surround(tokens, |tokens| { self.fields.to_tokens(tokens); if let Some(dot2_token) = &self.dot2_token { diff --git a/src/item.rs b/src/item.rs index fa87b42b96..aaee5ede19 100644 --- a/src/item.rs +++ b/src/item.rs @@ -2894,6 +2894,8 @@ mod printing { UsePath, UseRename, Variadic, }; use crate::mac::MacroDelimiter; + use crate::path; + use crate::path::printing::PathStyle; use crate::print::TokensOrDefault; use crate::ty::Type; use proc_macro2::TokenStream; @@ -3135,7 +3137,7 @@ mod printing { impl ToTokens for ItemMacro { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); - self.mac.path.to_tokens(tokens); + path::printing::print_path(tokens, &self.mac.path, PathStyle::Mod); self.mac.bang_token.to_tokens(tokens); self.ident.to_tokens(tokens); match &self.mac.delimiter { diff --git a/src/mac.rs b/src/mac.rs index 7e1876c648..15107801cf 100644 --- a/src/mac.rs +++ b/src/mac.rs @@ -197,6 +197,8 @@ pub(crate) mod parsing { #[cfg(feature = "printing")] mod printing { use crate::mac::{Macro, MacroDelimiter}; + use crate::path; + use crate::path::printing::PathStyle; use crate::token; use proc_macro2::{Delimiter, TokenStream}; use quote::ToTokens; @@ -215,7 +217,7 @@ mod printing { #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for Macro { fn to_tokens(&self, tokens: &mut TokenStream) { - self.path.to_tokens(tokens); + path::printing::print_path(tokens, &self.path, PathStyle::Mod); self.bang_token.to_tokens(tokens); self.delimiter.surround(tokens, self.tokens.clone()); } diff --git a/src/pat.rs b/src/pat.rs index 07849c04b9..81a2f183f6 100644 --- a/src/pat.rs +++ b/src/pat.rs @@ -812,6 +812,7 @@ mod printing { PatTuple, PatTupleStruct, PatType, PatWild, }; use crate::path; + use crate::path::printing::PathStyle; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; @@ -880,7 +881,7 @@ mod printing { impl ToTokens for PatStruct { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); - path::printing::print_path(tokens, &self.qself, &self.path); + path::printing::print_qpath(tokens, &self.qself, &self.path, PathStyle::Expr); self.brace_token.surround(tokens, |tokens| { self.fields.to_tokens(tokens); // NOTE: We need a comma before the dot2 token if it is present. @@ -915,7 +916,7 @@ mod printing { impl ToTokens for PatTupleStruct { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); - path::printing::print_path(tokens, &self.qself, &self.path); + path::printing::print_qpath(tokens, &self.qself, &self.path, PathStyle::Expr); self.paren_token.surround(tokens, |tokens| { self.elems.to_tokens(tokens); }); diff --git a/src/path.rs b/src/path.rs index 636d5d5e8f..aaea57591b 100644 --- a/src/path.rs +++ b/src/path.rs @@ -702,33 +702,62 @@ pub(crate) mod printing { use quote::ToTokens; use std::cmp; + pub(crate) enum PathStyle { + Expr, + Mod, + AsWritten, + } + + impl Copy for PathStyle {} + + impl Clone for PathStyle { + fn clone(&self) -> Self { + *self + } + } + #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for Path { fn to_tokens(&self, tokens: &mut TokenStream) { - self.leading_colon.to_tokens(tokens); - self.segments.to_tokens(tokens); + print_path(tokens, self, PathStyle::AsWritten); + } + } + + pub(crate) fn print_path(tokens: &mut TokenStream, path: &Path, style: PathStyle) { + path.leading_colon.to_tokens(tokens); + for segment in path.segments.pairs() { + print_path_segment(tokens, segment.value(), style); + segment.punct().to_tokens(tokens); } } #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for PathSegment { fn to_tokens(&self, tokens: &mut TokenStream) { - self.ident.to_tokens(tokens); - self.arguments.to_tokens(tokens); + print_path_segment(tokens, self, PathStyle::AsWritten); } } + fn print_path_segment(tokens: &mut TokenStream, segment: &PathSegment, style: PathStyle) { + segment.ident.to_tokens(tokens); + print_path_arguments(tokens, &segment.arguments, style); + } + #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for PathArguments { fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - PathArguments::None => {} - PathArguments::AngleBracketed(arguments) => { - arguments.to_tokens(tokens); - } - PathArguments::Parenthesized(arguments) => { - arguments.to_tokens(tokens); - } + print_path_arguments(tokens, self, PathStyle::AsWritten); + } + } + + fn print_path_arguments(tokens: &mut TokenStream, arguments: &PathArguments, style: PathStyle) { + match arguments { + PathArguments::None => {} + PathArguments::AngleBracketed(arguments) => { + print_angle_bracketed_generic_arguments(tokens, arguments, style); + } + PathArguments::Parenthesized(arguments) => { + print_parenthesized_generic_arguments(tokens, arguments, style); } } } @@ -753,44 +782,56 @@ pub(crate) mod printing { #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for AngleBracketedGenericArguments { fn to_tokens(&self, tokens: &mut TokenStream) { - self.colon2_token.to_tokens(tokens); - self.lt_token.to_tokens(tokens); - - // Print lifetimes before types/consts/bindings, regardless of their - // order in self.args. - let mut trailing_or_empty = true; - for param in self.args.pairs() { - match param.value() { - GenericArgument::Lifetime(_) => { - param.to_tokens(tokens); - trailing_or_empty = param.punct().is_some(); - } - GenericArgument::Type(_) - | GenericArgument::Const(_) - | GenericArgument::AssocType(_) - | GenericArgument::AssocConst(_) - | GenericArgument::Constraint(_) => {} + print_angle_bracketed_generic_arguments(tokens, self, PathStyle::AsWritten); + } + } + + pub(crate) fn print_angle_bracketed_generic_arguments( + tokens: &mut TokenStream, + arguments: &AngleBracketedGenericArguments, + style: PathStyle, + ) { + if let PathStyle::Mod = style { + return; + } + + conditionally_print_turbofish(tokens, &arguments.colon2_token, style); + arguments.lt_token.to_tokens(tokens); + + // Print lifetimes before types/consts/bindings, regardless of their + // order in args. + let mut trailing_or_empty = true; + for param in arguments.args.pairs() { + match param.value() { + GenericArgument::Lifetime(_) => { + param.to_tokens(tokens); + trailing_or_empty = param.punct().is_some(); } + GenericArgument::Type(_) + | GenericArgument::Const(_) + | GenericArgument::AssocType(_) + | GenericArgument::AssocConst(_) + | GenericArgument::Constraint(_) => {} } - for param in self.args.pairs() { - match param.value() { - GenericArgument::Type(_) - | GenericArgument::Const(_) - | GenericArgument::AssocType(_) - | GenericArgument::AssocConst(_) - | GenericArgument::Constraint(_) => { - if !trailing_or_empty { - ::default().to_tokens(tokens); - } - param.to_tokens(tokens); - trailing_or_empty = param.punct().is_some(); + } + for param in arguments.args.pairs() { + match param.value() { + GenericArgument::Type(_) + | GenericArgument::Const(_) + | GenericArgument::AssocType(_) + | GenericArgument::AssocConst(_) + | GenericArgument::Constraint(_) => { + if !trailing_or_empty { + ::default().to_tokens(tokens); } - GenericArgument::Lifetime(_) => {} + param.to_tokens(tokens); + trailing_or_empty = param.punct().is_some(); } + GenericArgument::Lifetime(_) => {} } - - self.gt_token.to_tokens(tokens); } + + arguments.gt_token.to_tokens(tokens); } #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] @@ -826,18 +867,36 @@ pub(crate) mod printing { #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for ParenthesizedGenericArguments { fn to_tokens(&self, tokens: &mut TokenStream) { - self.paren_token.surround(tokens, |tokens| { - self.inputs.to_tokens(tokens); - }); - self.output.to_tokens(tokens); + print_parenthesized_generic_arguments(tokens, self, PathStyle::AsWritten); } } - pub(crate) fn print_path(tokens: &mut TokenStream, qself: &Option, path: &Path) { + fn print_parenthesized_generic_arguments( + tokens: &mut TokenStream, + arguments: &ParenthesizedGenericArguments, + style: PathStyle, + ) { + if let PathStyle::Mod = style { + return; + } + + conditionally_print_turbofish(tokens, &None, style); + arguments.paren_token.surround(tokens, |tokens| { + arguments.inputs.to_tokens(tokens); + }); + arguments.output.to_tokens(tokens); + } + + pub(crate) fn print_qpath( + tokens: &mut TokenStream, + qself: &Option, + path: &Path, + style: PathStyle, + ) { let qself = match qself { Some(qself) => qself, None => { - path.to_tokens(tokens); + print_path(tokens, path, style); return; } }; @@ -850,20 +909,31 @@ pub(crate) mod printing { TokensOrDefault(&qself.as_token).to_tokens(tokens); path.leading_colon.to_tokens(tokens); for (i, segment) in segments.by_ref().take(pos).enumerate() { + print_path_segment(tokens, segment.value(), PathStyle::AsWritten); if i + 1 == pos { - segment.value().to_tokens(tokens); qself.gt_token.to_tokens(tokens); - segment.punct().to_tokens(tokens); - } else { - segment.to_tokens(tokens); } + segment.punct().to_tokens(tokens); } } else { qself.gt_token.to_tokens(tokens); path.leading_colon.to_tokens(tokens); } for segment in segments { - segment.to_tokens(tokens); + print_path_segment(tokens, segment.value(), style); + segment.punct().to_tokens(tokens); + } + } + + fn conditionally_print_turbofish( + tokens: &mut TokenStream, + colon2_token: &Option, + style: PathStyle, + ) { + match style { + PathStyle::Expr => TokensOrDefault(colon2_token).to_tokens(tokens), + PathStyle::Mod => unreachable!(), + PathStyle::AsWritten => colon2_token.to_tokens(tokens), } } diff --git a/src/restriction.rs b/src/restriction.rs index 8a4d4706a5..6e6758f3cd 100644 --- a/src/restriction.rs +++ b/src/restriction.rs @@ -146,6 +146,8 @@ pub(crate) mod parsing { #[cfg(feature = "printing")] mod printing { + use crate::path; + use crate::path::printing::PathStyle; use crate::restriction::{VisRestricted, Visibility}; use proc_macro2::TokenStream; use quote::ToTokens; @@ -169,7 +171,7 @@ mod printing { // TODO: If we have a path which is not "self" or "super" or // "crate", automatically add the "in" token. self.in_token.to_tokens(tokens); - self.path.to_tokens(tokens); + path::printing::print_path(tokens, &self.path, PathStyle::Mod); }); } } diff --git a/src/ty.rs b/src/ty.rs index a1543be056..77ce616d1f 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -1018,6 +1018,7 @@ pub(crate) mod parsing { mod printing { use crate::attr::FilterAttrs; use crate::path; + use crate::path::printing::PathStyle; use crate::print::TokensOrDefault; use crate::ty::{ Abi, BareFnArg, BareVariadic, ReturnType, TypeArray, TypeBareFn, TypeGroup, TypeImplTrait, @@ -1116,7 +1117,7 @@ mod printing { #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] impl ToTokens for TypePath { fn to_tokens(&self, tokens: &mut TokenStream) { - path::printing::print_path(tokens, &self.qself, &self.path); + path::printing::print_qpath(tokens, &self.qself, &self.path, PathStyle::AsWritten); } }