const_format_proc_macros-0.2.32/.cargo_vcs_info.json0000644000000001660000000000100162100ustar { "git": { "sha1": "cc41c595d721f42b02e23d970fb40b17615f0aa6" }, "path_in_vcs": "const_format_proc_macros" }const_format_proc_macros-0.2.32/Cargo.toml0000644000000030100000000000100141750ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.57.0" name = "const_format_proc_macros" version = "0.2.32" authors = ["rodrimati1992 "] include = [ "Cargo.toml", "src/**/*.rs", "../README.md", "LICENSE-ZLIB.md", ] description = "Implementation detail of the `const_format` crate" keywords = [ "no-std", "format", "concat", ] categories = [ "no-std", "text-processing", ] license = "Zlib" repository = "https://github.com/rodrimati1992/const_format_crates/" resolver = "1" [package.metadata.docs.rs] rustc-args = [ "--cfg", "feature = \"all\"", ] [lib] proc-macro = true [dependencies.proc-macro2] version = "1.0.19" [dependencies.quote] version = "1.0.7" [dependencies.syn] version = "1.0.38" features = [ "parsing", "proc-macro", ] optional = true default_features = false [dependencies.unicode-xid] version = "0.2" [dev-dependencies.fastrand] version = "1.3.4" [features] all = ["derive"] debug = ["syn/extra-traits"] default = [] derive = [ "syn", "syn/derive", "syn/printing", ] const_format_proc_macros-0.2.32/Cargo.toml.orig000064400000000000000000000016531046102023000176710ustar 00000000000000[package] name = "const_format_proc_macros" version = "0.2.32" authors = ["rodrimati1992 "] rust-version = "1.57.0" edition = "2021" license = "Zlib" description = "Implementation detail of the `const_format` crate" keywords = ["no-std", "format", "concat"] categories = ["no-std", "text-processing"] repository = "https://github.com/rodrimati1992/const_format_crates/" include = [ "Cargo.toml", "src/**/*.rs", "../README.md", "LICENSE-ZLIB.md", ] [lib] proc-macro = true [features] default = [] derive = ["syn", "syn/derive", "syn/printing"] debug = ["syn/extra-traits"] all = ["derive"] [dependencies] quote = "1.0.7" proc-macro2 = "1.0.19" unicode-xid = "0.2" [dependencies.syn] version = "1.0.38" optional = true default_features = false features = ["parsing", "proc-macro"] [dev-dependencies] fastrand = "1.3.4" [package.metadata.docs.rs] rustc-args = ["--cfg", "feature = \"all\""] const_format_proc_macros-0.2.32/LICENSE-ZLIB.md000064400000000000000000000015271046102023000171440ustar 00000000000000Copyright (c) 2020 Matias Rodriguez. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.const_format_proc_macros-0.2.32/src/datastructure/field_map.rs000064400000000000000000000051011046102023000227410ustar 00000000000000use super::*; use std::ops::{Index, IndexMut}; /** This is a map from fields to some value. If you put this in a type,and use Default to initialize it, you must remember to replace the `FieldMap` using either `FieldMap::defaulted` or `FieldMap::with` */ #[derive(Debug)] pub struct FieldMap { // The outer vec is the enum variant (if it's a struct/union it's a single element Vec), // the inner one is the field within a variant/struct/union. fields: Vec>, } impl FieldMap { /// Constructs an FieldMap which maps each field in the DataStructure to a value /// (obtained by mapping each individual field to `T` using a closure). pub fn with<'a, F>(ds: &'a DataStructure<'a>, mut f: F) -> Self where F: FnMut(&'a Field<'a>) -> T, { Self { fields: ds .variants .iter() .map(|vari| vari.fields.iter().map(&mut f).collect::>()) .collect::>(), } } #[allow(dead_code)] pub fn iter(&self) -> impl Iterator + Clone + '_ { self.fields.iter().enumerate().flat_map(|(v_i, v)| { v.iter().enumerate().map(move |(f_i, f)| { let index = FieldIndex { variant: v_i as _, pos: f_i as _, }; (index, f) }) }) } #[allow(dead_code)] pub fn iter_mut(&mut self) -> impl Iterator + '_ { self.fields.iter_mut().enumerate().flat_map(|(v_i, v)| { v.iter_mut().enumerate().map(move |(f_i, f)| { let index = FieldIndex { variant: v_i as _, pos: f_i as _, }; (index, f) }) }) } } impl<'a, T> Index for FieldMap { type Output = T; fn index(&self, index: FieldIndex) -> &T { &self.fields[index.variant][index.pos] } } impl<'a, T> IndexMut for FieldMap { fn index_mut(&mut self, index: FieldIndex) -> &mut T { &mut self.fields[index.variant][index.pos] } } impl<'a, T> Index<&'a Field<'a>> for FieldMap { type Output = T; fn index(&self, field: &'a Field<'a>) -> &T { let index = field.index; &self.fields[index.variant][index.pos] } } impl<'a, T> IndexMut<&'a Field<'a>> for FieldMap { fn index_mut(&mut self, field: &'a Field<'a>) -> &mut T { let index = field.index; &mut self.fields[index.variant][index.pos] } } const_format_proc_macros-0.2.32/src/datastructure.rs000064400000000000000000000164541046102023000210360ustar 00000000000000use syn::{ self, Attribute, Data, DeriveInput, Field as SynField, Fields as SynFields, Generics, Ident, Type, Visibility, }; use quote::ToTokens; use proc_macro2::TokenStream; use std::fmt::{self, Display}; mod field_map; pub use self::field_map::FieldMap; ////////////////////////////////////////////////////////////////////////////// /// A type definition(enum,struct,union). #[derive(Clone)] pub struct DataStructure<'a> { pub vis: &'a Visibility, pub name: &'a Ident, pub generics: &'a Generics, pub attrs: &'a [Attribute], /// Whether this is a struct/union/enum. pub data_variant: DataVariant, /// The variants in the type definition. /// /// If it is a struct or a union this only has 1 element. pub variants: Vec>, } impl<'a> DataStructure<'a> { pub fn new(ast: &'a DeriveInput) -> Self { let name = &ast.ident; let data_variant: DataVariant; let mut variants = Vec::new(); match &ast.data { Data::Enum(enum_) => { for (variant, var) in enum_.variants.iter().enumerate() { variants.push(Struct::new( StructParams { variant: variant, attrs: &var.attrs, name: &var.ident, }, &var.fields, )); } data_variant = DataVariant::Enum; } Data::Struct(struct_) => { variants.push(Struct::new( StructParams { variant: 0, attrs: &[], name: name, }, &struct_.fields, )); data_variant = DataVariant::Struct; } Data::Union(union_) => { let fields = Some(&union_.fields.named); let sk = StructKind::Braced; let vari = Struct::with_fields( StructParams { variant: 0, attrs: &[], name: name, }, sk, fields, ); variants.push(vari); data_variant = DataVariant::Union; } } Self { vis: &ast.vis, name, attrs: &ast.attrs, generics: &ast.generics, data_variant, variants, } } } ////////////////////////////////////////////////////////////////////////////// /// Whether the struct is tupled or not. #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)] pub enum StructKind { /// structs declared using the `struct Name( ... ) syntax. Tupled, /// structs declared using the `struct Name{ ... }` or `struct name;` syntaxes Braced, } #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)] pub enum DataVariant { Struct, Enum, Union, } #[derive(Copy, Clone)] pub struct FieldIndex { pub variant: usize, pub pos: usize, } ////////////////////////////////////////////////////////////////////////////// #[derive(Copy, Clone)] struct StructParams<'a> { variant: usize, attrs: &'a [Attribute], name: &'a Ident, } /// A struct/union or a variant of an enum. #[derive(Clone)] pub struct Struct<'a> { /// The attributes of this `Struct`. /// /// If this is a struct/union:these is the same as DataStructure.attrs. /// /// If this is an enum:these are the attributes on the variant. pub attrs: &'a [Attribute], /// The name of this `Struct`. /// /// If this is a struct/union:these is the same as DataStructure.name. /// /// If this is an enum:this is the name of the variant. pub name: &'a Ident, pub kind: StructKind, pub fields: Vec>, _priv: (), } impl<'a> Struct<'a> { fn new(p: StructParams<'a>, fields: &'a SynFields) -> Self { let kind = match *fields { SynFields::Named { .. } => StructKind::Braced, SynFields::Unnamed { .. } => StructKind::Tupled, SynFields::Unit { .. } => StructKind::Braced, }; let fields = match fields { SynFields::Named(f) => Some(&f.named), SynFields::Unnamed(f) => Some(&f.unnamed), SynFields::Unit => None, }; Self::with_fields(p, kind, fields) } fn with_fields(p: StructParams<'a>, kind: StructKind, fields: Option) -> Self where I: IntoIterator, { let fields = match fields { Some(x) => Field::from_iter(p, x), None => Vec::new(), }; Self { attrs: p.attrs, name: p.name, kind, fields, _priv: (), } } } ////////////////////////////////////////////////////////////////////////////// /// Represent a struct field /// #[derive(Clone)] pub struct Field<'a> { pub index: FieldIndex, pub attrs: &'a [Attribute], /// identifier for the field,which is either an index(in a tuple struct) or a name. pub ident: FieldIdent<'a>, pub pattern_ident: Ident, pub ty: &'a Type, } impl<'a> Field<'a> { fn new(index: FieldIndex, field: &'a SynField) -> Self { let span; let ident = match field.ident.as_ref() { Some(ident) => { span = ident.span(); FieldIdent::Named(ident) } None => { span = syn::spanned::Spanned::span(&field.ty); FieldIdent::new_index(index.pos) } }; let pattern_ident = Ident::new(&format!("f{}_7ac4rtizw8q", ident), span); Self { index, attrs: &field.attrs, ident, pattern_ident, ty: &field.ty, } } /// Gets the identifier of this field as an `&Ident`. pub fn pattern_ident(&self) -> &Ident { &self.pattern_ident } fn from_iter(p: StructParams<'a>, fields: I) -> Vec where I: IntoIterator, { fields .into_iter() .enumerate() .map(|(pos, f)| { let fi = FieldIndex { variant: p.variant, pos, }; Field::new(fi, f) }) .collect() } } ////////////////////////////////////////////////////////////////////////////// #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum FieldIdent<'a> { Index(usize), Named(&'a Ident), } impl<'a> Display for FieldIdent<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FieldIdent::Index(x) => Display::fmt(x, f), FieldIdent::Named(x) => Display::fmt(x, f), } } } impl<'a> ToTokens for FieldIdent<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { match *self { FieldIdent::Index(ind) => syn::Index::from(ind).to_tokens(tokens), FieldIdent::Named(name) => name.to_tokens(tokens), } } } impl<'a> FieldIdent<'a> { fn new_index(index: usize) -> Self { FieldIdent::Index(index) } } const_format_proc_macros-0.2.32/src/derive_debug/attribute_parsing.rs000064400000000000000000000225201046102023000243050ustar 00000000000000use crate::{ datastructure::{DataStructure, Field, FieldMap}, utils::LinearResult, }; use super::{syntax::ImplHeader, type_detection}; use quote::ToTokens; use syn::{Attribute, Meta, MetaList, NestedMeta}; use std::marker::PhantomData; pub(crate) struct ConstDebugConfig<'a> { pub(crate) debug_print: bool, pub(crate) crate_path: Option, pub(crate) impls: Vec, pub(crate) field_map: FieldMap>, _marker: PhantomData<&'a ()>, } impl<'a> ConstDebugConfig<'a> { fn new(roa: ConstDebugAttrs<'a>) -> Result { let ConstDebugAttrs { debug_print, crate_path, impls, field_map, errors: _, _marker: PhantomData, } = roa; Ok(Self { debug_print, crate_path, impls, field_map, _marker: PhantomData, }) } } struct ConstDebugAttrs<'a> { debug_print: bool, crate_path: Option, impls: Vec, field_map: FieldMap>, errors: LinearResult, _marker: PhantomData<&'a ()>, } //////////////////////////////////////////////////////////////////////////////// pub(crate) struct FieldConfig<'a> { pub(crate) how_to_fmt: HowToFmt<'a>, } pub(crate) enum HowToFmt<'a> { /// coerce_to_fmt!(&field).const_debug_fmt(f)` Regular, /// Doesn't print the field. Ignore, /// A slice or an array Slice, /// A single field tuple struct, èg: `struct Foo(u32);;` Option_, /// A single field tuple struct, èg: `struct Foo(u32);;` /// The path of the field is parsed from the type, erroring if it's not a path. Newtype(&'a syn::Ident), /// The function used to format the field. With(syn::Path), //// The macro used to format the field, //// it's expected to be callable as `themacro!(var, formatter);`. WithMacro(syn::Path), /// The newtype used to format the field, taking the field by reference. /// eg: `struct Foo<'a>(&'a u32);`. WithWrapper(syn::Path), } //////////////////////////////////////////////////////////////////////////////// #[derive(Copy, Clone)] enum ParseContext<'a> { TypeAttr, Field { field: &'a Field<'a> }, } pub(crate) fn parse_attrs_for_derive<'a>( ds: &'a DataStructure<'a>, ) -> Result, crate::Error> { let mut this = ConstDebugAttrs { debug_print: false, crate_path: None, impls: Vec::new(), field_map: FieldMap::with(ds, |f| FieldConfig { how_to_fmt: type_detection::detect_type_formatting(f.ty), }), errors: LinearResult::ok(), _marker: PhantomData, }; let ty_ctx = ParseContext::TypeAttr; parse_inner(&mut this, ds.attrs, ty_ctx)?; for variant in &ds.variants { for field in variant.fields.iter() { parse_inner(&mut this, field.attrs, ParseContext::Field { field })?; } } this.errors.take()?; ConstDebugConfig::new(this) } /// Parses an individual attribute fn parse_inner<'a, I>( this: &mut ConstDebugAttrs<'a>, attrs: I, pctx: ParseContext<'a>, ) -> Result<(), crate::Error> where I: IntoIterator, { for attr in attrs { match attr.parse_meta() { Ok(Meta::List(list)) => { let x = parse_attr_list(this, pctx, list); this.errors.combine_err(x); } Err(e) => { this.errors.push_err(e); } _ => {} } } Ok(()) } /// Parses an individual attribute list (A `#[attribute( .. )] attribute`). fn parse_attr_list<'a>( this: &mut ConstDebugAttrs<'a>, pctx: ParseContext<'a>, list: MetaList, ) -> Result<(), crate::Error> { if list.path.is_ident("cdeb") { with_nested_meta("cdeb", list.nested, |attr| { let x = parse_sabi_attr(this, pctx, attr); this.errors.combine_err(x); Ok(()) })?; } Ok(()) } fn make_err(tokens: &dyn ToTokens) -> crate::Error { spanned_err!(tokens, "unrecognized attribute") } /// Parses the contents of a `#[sabi( .. )]` attribute. fn parse_sabi_attr<'a>( this: &mut ConstDebugAttrs<'a>, pctx: ParseContext<'a>, attr: Meta, ) -> Result<(), crate::Error> { match (pctx, attr) { (ParseContext::Field { field, .. }, Meta::Path(path)) => { let f_config = &mut this.field_map[field.index]; if path.is_ident("ignore") { f_config.how_to_fmt = HowToFmt::Ignore; } else { return Err(make_err(&path)); } } (ParseContext::Field { field, .. }, Meta::NameValue(nv)) => { let f_config = &mut this.field_map[field.index]; if nv.path.is_ident("with") { f_config.how_to_fmt = HowToFmt::With(parse_lit(&nv.lit)?); } else if nv.path.is_ident("with_macro") { f_config.how_to_fmt = HowToFmt::WithMacro(parse_lit(&nv.lit)?); } else if nv.path.is_ident("with_wrapper") { f_config.how_to_fmt = HowToFmt::WithWrapper(parse_lit(&nv.lit)?); } else { return Err(make_err(&nv)); } } (ParseContext::Field { field, .. }, Meta::List(list)) => { let f_config = &mut this.field_map[field.index]; if list.path.is_ident("is_a") { match list.nested.len() { 0 => return Err(make_err(&list)), 1 => (), _ => return_spanned_err!( list, "The `#[cdeb(is_a())` attribute must only specify one kind of type." ), } with_nested_meta("is_a", list.nested, |attr| { f_config.how_to_fmt = parse_the_is_a_attribute(attr, field)?; Ok(()) })?; } else { return Err(make_err(&list)); } } (ParseContext::TypeAttr { .. }, Meta::Path(path)) => { if path.is_ident("debug_print") { this.debug_print = true; } else { return Err(make_err(&path)); } } (ParseContext::TypeAttr { .. }, Meta::NameValue(nv)) => { if nv.path.is_ident("crate") { this.crate_path = Some(parse_lit(&nv.lit)?); } else { return Err(make_err(&nv)); } } (ParseContext::TypeAttr { .. }, Meta::List(list)) => { if list.path.is_ident("impls") { for x in list.nested { let lit = match x { NestedMeta::Meta(attr) => return Err(make_err(&attr)), NestedMeta::Lit(lit) => lit, }; this.impls.push(parse_lit::(&lit)?); } } else { return Err(make_err(&list)); } } #[allow(unreachable_patterns)] (_, x) => return Err(make_err(&x)), } Ok(()) } /////////////////////////////////////////////////////////////////////////////// fn parse_the_is_a_attribute<'a>( attr: syn::Meta, f: &Field<'a>, ) -> Result, crate::Error> { match attr { Meta::Path(path) => { if path.is_ident("array") || path.is_ident("slice") { Ok(HowToFmt::Slice) } else if path.is_ident("Option") || path.is_ident("option") { Ok(HowToFmt::Option_) } else if path.is_ident("newtype") { let newtype = type_detection::parse_type_as_ident(f.ty)?; Ok(HowToFmt::Newtype(newtype)) } else if path.is_ident("non_std") || path.is_ident("not_std") { Ok(HowToFmt::Regular) } else { Err(make_err(&path)) } } _ => Err(make_err(&attr)), } } /////////////////////////////////////////////////////////////////////////////// fn parse_lit(lit: &syn::Lit) -> Result where T: syn::parse::Parse, { match lit { syn::Lit::Str(x) => x.parse().map_err(crate::Error::from), _ => Err(spanned_err!( lit, "Expected string literal containing identifier" )), } } #[allow(dead_code)] fn parse_expr(lit: syn::Lit) -> Result { match lit { syn::Lit::Str(x) => x.parse(), syn::Lit::Int(x) => syn::parse_str(x.base10_digits()), _ => return_spanned_err!(lit, "Expected string or integer literal"), } .map_err(crate::Error::from) } /////////////////////////////////////////////////////////////////////////////// pub fn with_nested_meta( attr_name: &str, iter: syn::punctuated::Punctuated, mut f: F, ) -> Result<(), crate::Error> where F: FnMut(Meta) -> Result<(), crate::Error>, { for repr in iter { match repr { NestedMeta::Meta(attr) => { f(attr)?; } NestedMeta::Lit(lit) => { return_spanned_err!( lit, "the #[{}(...)] attribute does not allow literals in the attribute list", attr_name, ); } } } Ok(()) } const_format_proc_macros-0.2.32/src/derive_debug/syntax.rs000064400000000000000000000010221046102023000220770ustar 00000000000000use syn::{ parse::{Parse, ParseStream}, Generics, }; //////////////////////////////////////////////////////////////////////////////// pub(crate) struct ImplHeader { pub(crate) generics: Generics, pub(crate) self_ty: syn::Path, } impl Parse for ImplHeader { fn parse(input: ParseStream) -> Result { let mut generics = input.parse::()?; let self_ty = input.parse()?; generics.where_clause = input.parse()?; Ok(Self { generics, self_ty }) } } const_format_proc_macros-0.2.32/src/derive_debug/type_detection.rs000064400000000000000000000030061046102023000235740ustar 00000000000000use super::HowToFmt; use syn::Type; pub(super) fn detect_type_formatting(ty: &Type) -> HowToFmt { let ty = unwrap_reference(ty); // println!("{:?} {}", ty, ty.to_token_stream()); match ty { Type::Array { .. } | Type::Slice { .. } => HowToFmt::Slice, Type::Path(ty) if ty.qself.is_none() && is_path_an_option(&ty.path) => HowToFmt::Option_, _ => HowToFmt::Regular, } } fn unwrap_reference(mut ty: &Type) -> &Type { loop { match ty { Type::Reference(next) => ty = &*next.elem, Type::Group(next) => ty = &*next.elem, Type::Paren(next) => ty = &*next.elem, _ => break, } } ty } fn is_path_an_option(path: &syn::Path) -> bool { let segments = &path.segments; if segments[0].ident == "Option" { return true; } if segments.len() < 3 { return false; } let thecrate = &segments[0].ident; (thecrate == "core" || thecrate == "alloc" || thecrate == "std") && segments[1].ident == "option" && segments[2].ident == "Option" } /// Parses the type as an identifier, or the last identifier of a path. pub(super) fn parse_type_as_ident(ty: &Type) -> Result<&syn::Ident, crate::Error> { let ty = unwrap_reference(ty); match ty { Type::Path(typath) if typath.qself.is_none() && !typath.path.segments.is_empty() => { Ok(&typath.path.segments.last().unwrap().ident) } _ => Err(spanned_err!(ty, "expected a struct or enum")), } } const_format_proc_macros-0.2.32/src/derive_debug.rs000064400000000000000000000207071046102023000205640ustar 00000000000000use crate::datastructure::{DataStructure, DataVariant, Field, StructKind}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, quote_spanned, quote_spanned as quote_s, ToTokens, TokenStreamExt}; use syn::{DeriveInput, Ident}; mod attribute_parsing; mod syntax; mod type_detection; use self::attribute_parsing::HowToFmt; pub(crate) fn derive_constdebug_impl(input: DeriveInput) -> Result { let ds = &DataStructure::new(&input); let config = attribute_parsing::parse_attrs_for_derive(ds)?; let cratep = match &config.crate_path { Some(p) => p.to_token_stream(), None => quote!(::const_format), }; let vis = ds.vis; let name = ds.name; let mut impl_headers; if config.impls.is_empty() { let impl_params = ds.generics.params.iter(); let (_, tygen, _) = ds.generics.split_for_impl(); let where_clause = get_where_clause_tokens(&ds.generics.where_clause); impl_headers = quote!( impl[#( #impl_params ,)*] #name #tygen #where_clause; ); } else { impl_headers = TokenStream2::new(); for imp in config.impls.iter() { let params = imp.generics.params.iter(); let self_ty = &imp.self_ty; let where_clause = get_where_clause_tokens(&imp.generics.where_clause); impl_headers.append_all(quote!( impl[#(#params)*] #self_ty #where_clause; )); } }; let enum_prefix = match ds.data_variant { DataVariant::Enum => quote!(#name::), DataVariant::Struct => TokenStream2::new(), DataVariant::Union => panic!("Cannot derive ConstDebug on unions"), }; let variant_branches = ds.variants.iter().map(|variant| { let vname = variant.name; let debug_method = match variant.kind { StructKind::Braced => Ident::new("debug_struct", Span::call_site()), StructKind::Tupled => Ident::new("debug_tuple", Span::call_site()), }; let patt = variant .fields .iter() .filter_map(|f| -> Option { if let HowToFmt::Ignore = config.field_map[f].how_to_fmt { return None; } let pat = &f.ident; let variable = f.pattern_ident(); Some(quote!(#pat : #variable,)) }); let fmt_call = variant .fields .iter() .filter_map(|f| -> Option { let how_to_fmt = &config.field_map[f].how_to_fmt; if let HowToFmt::Ignore = how_to_fmt { return None; } let fspan = f.pattern_ident().span(); let field_name_str = match variant.kind { StructKind::Braced => Some(f.ident.to_string()), StructKind::Tupled => None, } .into_iter(); let mut field_ts = quote_spanned!(fspan=> let mut field_formatter = formatter.field(#(#field_name_str)*); ); field_ts.append_all(match &how_to_fmt { HowToFmt::Regular => coerce_and_fmt(&cratep, f), HowToFmt::Ignore => unreachable!(), HowToFmt::Slice => fmt_slice(&cratep, f), HowToFmt::Option_ => fmt_option(&cratep, f), HowToFmt::Newtype(newtype) => fmt_newtype(&cratep, newtype, f), HowToFmt::With(with) => call_with_function(&cratep, f, with), HowToFmt::WithMacro(with) => call_with_macro(&cratep, f, with), HowToFmt::WithWrapper(with) => call_with_wrapper(&cratep, f, with), }); Some(field_ts) }); quote!( #enum_prefix #vname { #(#patt)* .. } => { let mut formatter = formatter.#debug_method(stringify!(#vname)); #(#fmt_call)* formatter.finish() } ) }); let ret = quote!( #cratep::impl_fmt!{ #impl_headers #vis const fn const_debug_fmt( &self, formatter: &mut #cratep::pmr::Formatter<'_>, ) -> #cratep::pmr::Result<(), #cratep::pmr::Error> { match self { #( #variant_branches )* } } } ); if config.debug_print { panic!("\n\n\n{}\n\n\n", ret); } Ok(ret) } // Copying the definitino of the `const_format::coerce_to_fn` macro here // because the compiler points inside the coerce_to_fn macro otherwise fn coerce_and_fmt(cratep: &TokenStream2, field: &Field<'_>) -> TokenStream2 { let var = field.pattern_ident(); let fspan = var.span(); quote_spanned!(fspan=> let mut marker = #cratep::pmr::IsAFormatMarker::NEW; if false { marker = marker.infer_type(#var); } #cratep::try_!( marker.coerce(marker.unreference(#var)) .const_debug_fmt(field_formatter) ); ) } fn fmt_slice(cratep: &TokenStream2, field: &Field<'_>) -> TokenStream2 { let var = field.pattern_ident(); let fspan = var.span(); let call = call_debug_fmt( cratep, quote_s!(fspan=> &#var[n]), quote_s!(fspan=> slice_fmt.entry()), fspan, ); quote_spanned!(fspan=>{ let mut slice_fmt = field_formatter.debug_list(); let mut n = 0; let len = #var.len(); while n != len { #call n += 1; } #cratep::try_!(slice_fmt.finish()); }) } fn fmt_option(cratep: &TokenStream2, field: &Field<'_>) -> TokenStream2 { let var = field.pattern_ident(); let fspan = var.span(); let call = call_debug_fmt( cratep, quote_s!(fspan=>val), quote_s!(fspan=>f.field()), fspan, ); quote_spanned!(fspan=> #cratep::try_!(match #var { #cratep::pmr::Some(val) => { let mut f = field_formatter.debug_tuple("Some"); #call f.finish() } #cratep::pmr::None => field_formatter.write_str("None"), }); ) } fn fmt_newtype(cratep: &TokenStream2, newtype: &syn::Ident, field: &Field<'_>) -> TokenStream2 { let var = field.pattern_ident(); let fspan = var.span(); let ty_str = newtype.to_token_stream().to_string(); let call = call_debug_fmt( cratep, quote_s!(fspan=> &#var.0 ), quote_s!(fspan=> f.field() ), fspan, ); quote_spanned!(fspan=>{ #cratep::try_!({ let mut f = field_formatter.debug_tuple(#ty_str); #call f.finish() }); }) } fn call_with_function(cratep: &TokenStream2, field: &Field<'_>, func: &syn::Path) -> TokenStream2 { let var = field.pattern_ident(); let fspan = var.span(); quote_spanned!(fspan=> #cratep::try_!(#func(#var, field_formatter)); ) } fn call_with_macro(cratep: &TokenStream2, field: &Field<'_>, macr: &syn::Path) -> TokenStream2 { let var = field.pattern_ident(); let fspan = var.span(); quote_spanned!(fspan=> #cratep::try_!(#macr!(#var, field_formatter)); ) } fn call_with_wrapper( cratep: &TokenStream2, field: &Field<'_>, newtype: &syn::Path, ) -> TokenStream2 { let var = field.pattern_ident(); let fspan = var.span(); quote_spanned!(fspan=> #cratep::try_!(#newtype(#var).const_debug_fmt(field_formatter)); ) } // Helper of the other `call_` functions fn call_debug_fmt( cratep: &TokenStream2, field: impl ToTokens, formatter: impl ToTokens, span: Span, ) -> TokenStream2 { quote_spanned!(span=>{ // Importing it like this because the error span is wrong otherwise use #cratep::pmr::IsAFormatMarker as __IsAFormatMarker; let mut marker = __IsAFormatMarker::NEW; if false { marker = marker.infer_type(#field); } #cratep::try_!( marker.coerce(marker.unreference(#field)).const_debug_fmt(#formatter) ); }) } fn get_where_clause_tokens(where_clause: &Option) -> TokenStream2 { match where_clause { Some(x) => { let preds = x.predicates.iter(); quote!(where[ #(#preds,)* ]) } None => TokenStream2::new(), } } const_format_proc_macros-0.2.32/src/error.rs000064400000000000000000000063211046102023000172650ustar 00000000000000use crate::spanned::Spans; use proc_macro2::{ Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream as TokenStream2, TokenTree as TokenTree2, }; use std::fmt::Display; #[derive(Debug, Clone)] pub struct Error { messages: Vec, } #[derive(Debug, Clone)] enum CompileError { Basic { start_span: Span, end_span: Span, msg: String, }, #[cfg(feature = "derive")] Syn(TokenStream2), } impl Error { pub fn new(span: Span, msg: T) -> Self { Error { messages: vec![CompileError::Basic { start_span: span, end_span: span, msg: msg.to_string(), }], } } pub fn spanned(spans: Spans, msg: T) -> Self { Error { messages: vec![CompileError::Basic { start_span: spans.start, end_span: spans.end, msg: msg.to_string(), }], } } pub fn to_compile_error(&self) -> TokenStream2 { macro_rules! tokenstream{ ($($tt:expr),* $(,)*) => ({ let list: Vec = vec![ $($tt.into(),)* ]; list.into_iter().collect::() }) } self.messages .iter() .map(|em| match em { CompileError::Basic { start_span, end_span, msg, } => { let ts = tokenstream![ Ident::new("compile_error", *start_span), { let mut this = Punct::new('!', Spacing::Alone); this.set_span(*start_span); this }, { let mut group = Group::new( Delimiter::Parenthesis, tokenstream![{ let mut lit = Literal::string(msg); lit.set_span(*end_span); TokenTree2::Literal(lit) }], ); group.set_span(*end_span); group }, ]; // Still have no idea why the compile_error has to be wrapped in parentheses // so that the spans point at the stuff between start_span and end_span. let mut this = Group::new(Delimiter::Parenthesis, ts); this.set_span(*end_span); tokenstream![this] } #[cfg(feature = "derive")] CompileError::Syn(x) => x.clone(), }) .collect() } pub fn combine(&mut self, another: Error) { self.messages.extend(another.messages) } } #[cfg(feature = "derive")] impl From for Error { fn from(err: syn::Error) -> Self { Self { messages: vec![CompileError::Syn(err.to_compile_error())], } } } const_format_proc_macros-0.2.32/src/format_args/parsing.rs000064400000000000000000000272101046102023000221030ustar 00000000000000use super::{ ExpandFormatted, ExpandInto, ExpandWithFormatter, FormatArg, FormatArgs, FormatIfArgs, LocalVariable, UncheckedFormatArg, UncheckedFormatArgs, WriteArgs, }; use crate::{ format_str::{FmtArg, FmtStrComponent, FormatStr, WhichArg}, parse_utils::{LitStr, MyParse, ParseBuffer, ParseStream, TokenTreeExt}, shared_arg_parsing::ExprArg, spanned::Spans, utils::{dummy_ident, LinearResult}, }; use proc_macro2::{Ident, Span, TokenTree}; //////////////////////////////////////////////// impl MyParse for UncheckedFormatArg { fn parse(input: ParseStream<'_>) -> Result { // the compile wraps `:expr` in macro_rules macros in a TokenStream::Group // with no delimiters. input.parse_unwrap_group(|content| { let mut ident = None; if matches!(content.peek2(), Some(x) if x.is_punct('=')) { ident = Some(content.parse_ident()?); content.next(); } // For some reason, // the compile wraps closures in parentheses when passing them as // expressions to proc macros. content.parse_unwrap_paren(|content| { let mut fmt_ident = None; if matches!(content.peek(), Some(x) if x.is_punct('|')) && matches!(content.peek2(), Some(TokenTree::Ident(_))) { content.next(); fmt_ident = Some(content.parse_ident()?); content.parse_punct('|')?; } let (expr, spans) = if content.peek2().is_some() { content.parse_token_stream_and_span() } else { content.parse_unwrap_tt(|content| Ok(content.parse_token_stream_and_span()))? }; Ok(Self { spans, ident, fmt_ident, expr, }) }) }) } } //////////////////////////////////////////////// fn lit_str_to_fmt_lit(lit: &LitStr) -> Result { let lit_str = lit.value(); let format_str_span = lit.span; FormatStr::parse(lit.value(), lit.rawness) .map_err(|e| e.into_crate_err(format_str_span, lit_str)) } fn parse_fmt_lit(this: &mut FormatStr, input: ParseStream<'_>) -> Result<(), crate::Error> { input.parse_unwrap_tt(|input| { let tt = input.next(); match tt { Some(TokenTree::Literal(lit)) => { let mut lit = lit_str_to_fmt_lit(&LitStr::parse_from_literal(&lit)?)?; this.list.append(&mut lit.list); Ok(()) } Some(TokenTree::Ident(ident)) if ident == "concat" => { input.next(); // skipping the `!` let paren = input.parse_paren()?; let mut input = ParseBuffer::new(paren.contents); while !input.is_empty() { parse_fmt_lit(this, &mut input)?; input.parse_opt_punct(',')?; } Ok(()) } _ => Ok(()), } }) } impl MyParse for UncheckedFormatArgs { fn parse(input: ParseStream<'_>) -> Result { let mut literal = FormatStr { list: Vec::new() }; // Have to parse `concat!()` because it's not expanded before the proc macro is called. { let paren = input.parse_paren()?; let mut input = ParseBuffer::new(paren.contents); parse_fmt_lit(&mut literal, &mut input)?; } input.parse_opt_punct(',')?; let mut args = Vec::new(); while !input.is_empty() { args.push(UncheckedFormatArg::parse(input)?); input.parse_opt_punct(',')?; } Ok(Self { literal, args }) } } //////////////////////////////////////////////// impl MyParse for FormatArgs { fn parse(input: ParseStream<'_>) -> Result { let prefix = Ident::new("const_fmt_local_", Span::call_site()); FormatArgs::parse_with(input, prefix) } } impl FormatArgs { pub fn parse_with(input: ParseStream<'_>, prefix: Ident) -> Result { let mut res = LinearResult::ok(); let unchecked_fargs = UncheckedFormatArgs::parse(input)?; let mut first_named_arg = unchecked_fargs.args.len(); let mut named_arg_names = Vec::::new(); let mut args = Vec::::with_capacity(unchecked_fargs.args.len()); let mut local_variables = Vec::::with_capacity(unchecked_fargs.args.len()); let arg_span_idents: Vec<(Spans, Option)> = unchecked_fargs .args .iter() .map(|x| (x.spans, x.ident.clone())) .collect(); { let mut prev_is_named_arg = false; for (i, arg) in unchecked_fargs.args.into_iter().enumerate() { let expr_span = arg.spans; let make_ident = |s: String| Ident::new(&s, expr_span.start); let is_named_arg = arg.ident.is_some(); let var_name = if let Some(ident) = arg.ident { if !prev_is_named_arg { first_named_arg = i; } let name = make_ident(format!("{}{}", prefix, ident)); named_arg_names.push(ident); name } else { if prev_is_named_arg { return Err(crate::Error::spanned( arg.spans, "expected a named argument, \ named arguments cannot be followed by positional arguments.", )); } make_ident(format!("{}{}", prefix, i)) }; let format_arg = if let Some(fmt_ident) = &arg.fmt_ident { FormatArg::WithFormatter { fmt_ident: fmt_ident.clone(), expr: arg.expr.clone(), } } else { local_variables.push(LocalVariable { ident: var_name.clone(), expr: arg.expr.clone(), }); FormatArg::WithLocal(var_name) }; args.push(format_arg); prev_is_named_arg = is_named_arg; } } let mut unused_args = vec![true; args.len()]; let first_named_arg = first_named_arg; let named_arg_names = named_arg_names; let args = args; let positional_args = &args[..first_named_arg]; let named_args = &args[first_named_arg..]; let fmt_str_components = unchecked_fargs.literal.list; let expanded_into: Vec = { let mut current_pos_arg = 0; let mut get_variable_name = |param: FmtArg| -> ExpandInto { let FmtArg { which_arg, formatting, rawness, } = param; let arg = match which_arg { WhichArg::Ident(ident) => { if let Some(pos) = named_arg_names.iter().position(|x| *x == ident) { unused_args[pos + first_named_arg] = false; &named_args[pos] } else { // `formatcp!("{FOO}")` assumes that FOO is a constant in scope return ExpandInto::Formatted(ExpandFormatted { local_variable: Ident::new(&ident, rawness.span()), format: formatting, }); } } WhichArg::Positional(opt_pos) => { let pos = opt_pos.unwrap_or_else(|| { let pos = current_pos_arg; current_pos_arg += 1; pos }); match positional_args.get(pos) { Some(arg) => { unused_args[pos] = false; arg } None => { res.push_err(crate::Error::new( rawness.span(), format!( "attempting to use nonexistent positional argument `{}`", pos, ), )); return ExpandInto::Formatted(ExpandFormatted { local_variable: dummy_ident(), format: formatting, }); } } } }; match arg { FormatArg::WithFormatter { fmt_ident, expr } => { ExpandInto::WithFormatter(ExpandWithFormatter { format: formatting, fmt_ident: fmt_ident.clone(), expr: expr.clone(), }) } FormatArg::WithLocal(local_variable) => { ExpandInto::Formatted(ExpandFormatted { format: formatting, local_variable: local_variable.clone(), }) } } }; fmt_str_components .into_iter() .map(|fmt_str_comp| match fmt_str_comp { FmtStrComponent::Str(str, str_rawness) => ExpandInto::Str(str, str_rawness), FmtStrComponent::Arg(arg) => get_variable_name(arg), }) .collect() }; for (i, (is_it_unused, (spans, ident))) in unused_args.iter().zip(&arg_span_idents).enumerate() { if *is_it_unused { let msg = if let Some(ident) = ident { format!("the '{}' argument is unused", ident) } else { format!("argument number {} is unused", i) }; res.push_err(crate::Error::spanned(*spans, msg)); } } res.take()?; Ok(FormatArgs { condition: None, local_variables, expanded_into, }) } } //////////////////////////////////////////////// impl MyParse for FormatIfArgs { fn parse(input: ParseStream) -> Result { let condition = ExprArg::parse(input)?; let mut inner = FormatArgs::parse(input)?; inner.condition = Some(condition); Ok(Self { inner }) } } //////////////////////////////////////////////// impl MyParse for WriteArgs { fn parse(input: ParseStream) -> Result { let prefix = Ident::new("const_fmt_local_", Span::call_site()); let paren = input.parse_paren()?; let mut content = ParseBuffer::new(paren.contents); let (writer_expr, spans) = content.parse_unwrap_tt(|content| Ok(content.parse_token_stream_and_span()))?; let format_args = FormatArgs::parse_with(input, prefix)?; Ok(Self { writer_expr, writer_span: spans.joined(), format_args, }) } } const_format_proc_macros-0.2.32/src/format_args.rs000064400000000000000000000075241046102023000204460ustar 00000000000000use crate::{ format_str::FormatStr, formatting::FormattingFlags, parse_utils::StrRawness, parse_utils::TokenStream2Ext, shared_arg_parsing::ExprArg, spanned::Spans, }; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote_spanned, TokenStreamExt}; //////////////////////////////////////////////// mod parsing; //////////////////////////////////////////////// struct UncheckedFormatArgs { literal: FormatStr, args: Vec, } struct UncheckedFormatArg { pub(crate) spans: Spans, pub(crate) ident: Option, // The identifier for the Formatter passed to format the argument. // If this is Some, then `expr` is expanded directly, pub(crate) fmt_ident: Option, /// Using a TokenStream2 because it is validated to be a valid expression in /// the macro_rules! macros that call these proc macros. pub(crate) expr: TokenStream2, } pub(crate) struct FormatArgs { pub(crate) condition: Option, pub(crate) local_variables: Vec, pub(crate) expanded_into: Vec, } pub(crate) struct FormatIfArgs { pub(crate) inner: FormatArgs, } /// The arguments of `writec` pub(crate) struct WriteArgs { pub(crate) writer_expr: TokenStream2, pub(crate) writer_span: Span, pub(crate) format_args: FormatArgs, } pub(crate) enum ExpandInto { Str(String, StrRawness), Formatted(ExpandFormatted), WithFormatter(ExpandWithFormatter), } pub(crate) struct ExpandFormatted { pub(crate) format: FormattingFlags, pub(crate) local_variable: Ident, } pub(crate) struct ExpandWithFormatter { pub(crate) format: FormattingFlags, pub(crate) fmt_ident: Ident, pub(crate) expr: TokenStream2, } pub(crate) struct LocalVariable { // The local variable that the macro will output for this argument, // so that it is not evaluated multiple times when it's used multiple times // in the format string. pub(crate) ident: Ident, /// Using a TokenStream2 because it is validated to be a valid expression in /// the macro_rules! macros that call these proc macros. pub(crate) expr: TokenStream2, } pub(crate) enum FormatArg { WithFormatter { // The identifier for the Formatter passed to format the argument. // If this is Some, then `expr` is expanded directly, fmt_ident: Ident, /// Using a TokenStream2 because it is validated to be a valid expression in /// the macro_rules! macros that call these proc macros. expr: TokenStream2, }, WithLocal(Ident), } //////////////////////////////////////////////// impl ExpandInto { pub(crate) fn fmt_call(&self, formatter: &Ident) -> TokenStream2 { match self { ExpandInto::Str(str, rawness) => { let str_tokens = rawness.tokenize_sub(str); quote_spanned!(rawness.span()=> #formatter.write_str(#str_tokens) ) } ExpandInto::Formatted(fmted) => { let flags = fmted.format; let fmt_method = fmted.format.fmt_method_name(); let local_variable = &fmted.local_variable; let span = local_variable.span(); let mut tokens = quote::quote!( __cf_osRcTFl4A::coerce_to_fmt!(&#local_variable) .#fmt_method ) .set_span_recursive(span); tokens.append_all(quote::quote!( (&mut #formatter.make_formatter(#flags)) )); tokens } ExpandInto::WithFormatter(ExpandWithFormatter { format, fmt_ident, expr, }) => quote::quote!({ let #fmt_ident = &mut #formatter.make_formatter(#format); __cf_osRcTFl4A::pmr::ToResult( #expr ).to_result() }), } } } const_format_proc_macros-0.2.32/src/format_macro/tests.rs000064400000000000000000000050251046102023000217470ustar 00000000000000use crate::{parse_utils::MyParse, test_utils::StrExt}; fn process_str(s: &str) -> Result { MyParse::parse_token_stream_2(s.parse().unwrap()) .and_then(crate::format_macro::formatcp_impl) .map(|x| x.to_string()) .map_err(|e| e.to_compile_error().to_string()) } macro_rules! assert_ret { ($call:expr, |$res:ident| $predicate:expr $(,)*) => { let $res = $call; let $res = $res.as_ref(); assert!($predicate, "\nreturned:\n{:?}\n\n", $res); }; } #[test] fn named_argument_followed_by_positional() { assert_ret!(process_str(r#"(""), (a=()), () "#), |s| { s.unwrap_err() .consecutive_in_self(&["named arguments", "cannot", "positional arguments"]) }); } #[test] fn access_formatter_error() { let cases = [ r#"("{}"), (|f| 100u8) "#, r#"("{}"), (|_| 100u8) "#, r#"("{foo}"), (foo = |f| 100u8) "#, r#"("{foo}"), (foo = |_| 100u8) "#, r#"("{foo}"), (foo = (|f| 100u8)) "#, r#"("{foo}"), (foo = (|_| 100u8)) "#, ]; for case in cases.iter().copied() { assert_ret!(process_str(case), |s| { s.unwrap_err().consecutive_in_self(&["custom formatting"]) }); } } #[test] fn nonexistent_argument() { assert_ret!(process_str(r#"("{1}"), () "#), |s| { s.unwrap_err() .consecutive_in_self(&["positional argument", "1"]) }); assert_ret!(process_str(r#"("{}{}"), () "#), |s| { s.unwrap_err() .consecutive_in_self(&["positional argument", "1"]) }); process_str(r#"("{}"), () "#).unwrap(); } #[test] fn unused_arguments() { assert_ret!(process_str(r#"("{}"), (), () "#), |s| { let e = s.unwrap_err(); e.consecutive_in_self(&["argument", "1", "unused"]) }); assert_ret!(process_str(r#"("{}"), (), () , () "#), |s| { let e = s.unwrap_err(); e.consecutive_in_self(&["argument", "1", "unused"]) && e.consecutive_in_self(&["argument", "2", "unused"]) }); assert_ret!(process_str(r#"("{}"), (), (foooo = "") "#), |s| { s.unwrap_err() .consecutive_in_self(&["foooo", "argument", "unused"]) }); assert_ret!( process_str(r#"("{}"), (), (), (foooo = ""), (bar = "") "#), |s| { let e = s.unwrap_err(); e.consecutive_in_self(&["argument", "1", "unused"]) && e.consecutive_in_self(&["foooo", "argument", "unused"]) && e.consecutive_in_self(&["bar", "argument", "unused"]) } ); } const_format_proc_macros-0.2.32/src/format_macro.rs000064400000000000000000000157531046102023000206160ustar 00000000000000use crate::{ format_args::{ExpandInto, FormatArgs, FormatIfArgs, LocalVariable, WriteArgs}, parse_utils::TokenStream2Ext, shared_arg_parsing::{ExprArg, ExprArgs}, Error, }; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, quote_spanned}; #[cfg(test)] mod tests; //////////////////////////////////////////////////////////////////////////////// pub(crate) fn concatcp_impl(value: ExprArgs) -> Result { let fmt_var = Ident::new("fmt", Span::mixed_site()); let concat_args = value.args.iter().map(|ExprArg { expr, span }| { quote_spanned!(span.start=> __cf_osRcTFl4A::pmr::PConvWrapper(#expr).to_pargument_display(#fmt_var) ) }); Ok(quote!(({ // The suffix is to avoid name collisions with identifiers in the passed-in expression. #[doc(hidden)] #[allow(unused_mut, non_snake_case)] const CONCATP_NHPMWYD3NJA : &[__cf_osRcTFl4A::pmr::PArgument] = { let #fmt_var = __cf_osRcTFl4A::pmr::FormattingFlags::NEW; &[ #( #concat_args ),* ] }; __cf_osRcTFl4A::__concatcp_inner!(CONCATP_NHPMWYD3NJA) }))) } //////////////////////////////////////////////////////////////////////////////// pub(crate) fn formatcp_if_macro_impl(value: FormatIfArgs) -> Result { formatcp_impl(value.inner) } pub(crate) fn formatcp_impl(fmt_args: FormatArgs) -> Result { let locals = fmt_args .local_variables .iter() .map(|LocalVariable { ident, expr }| { let span = ident.span(); quote_spanned!(span=> let #ident = #expr;) }); for ei in fmt_args.expanded_into.iter() { if let ExpandInto::WithFormatter(wf) = ei { return Err(crate::Error::new( wf.fmt_ident.span(), "Can't do custom formatting in the `formatcp` macro", )); } } let parg_constructor = fmt_args.expanded_into.iter().map(|ei| match ei { ExpandInto::Str(str, rawness) => { let str_tokens = rawness.tokenize_sub(str); quote!( __cf_osRcTFl4A::pmr::PConvWrapper(#str_tokens) .to_pargument_display(__cf_osRcTFl4A::pmr::FormattingFlags::NEW) ) } ExpandInto::Formatted(fmted) => { let to_pargument_m = fmted.format.to_pargument_method_name(); let formatting = fmted.format; let local_variable = &fmted.local_variable; let span = local_variable.span(); // I had to use `set_span_recursive` to set the span to that of the argument, // quote_span doesn't work for that somehow. quote!( __cf_osRcTFl4A::pmr::PConvWrapper(#local_variable).#to_pargument_m(#formatting) ) .set_span_recursive(span) } ExpandInto::WithFormatter { .. } => unreachable!(), }); let fmt_if_true = quote!({ let mut len = 0usize; #( #locals )* &[ #( #parg_constructor ),* ] }); if let Some(cond) = fmt_args.condition { Ok(quote!(({ enum __Foo_osRcTFl4A {} // This is generic so that the constant is only evaluated when it's needed. impl __cf_osRcTFl4A::pmr::ConcatArgsIf for __Foo_osRcTFl4A { #[doc(hidden)] const PARGUMENTS : &'static [__cf_osRcTFl4A::pmr::PArgument] = #fmt_if_true; } __cf_osRcTFl4A::__concatcp_inner!( <__Foo_osRcTFl4A as __cf_osRcTFl4A::pmr::ConcatArgsIf<(), #cond>>::PARGUMENTS ) }))) } else { Ok(quote!(({ // The suffix is to avoid name collisions with identifiers in the passed-in expression. #[doc(hidden)] #[allow(unused_mut, non_snake_case)] const CONCATP_NHPMWYD3NJA : &[__cf_osRcTFl4A::pmr::PArgument] = #fmt_if_true; __cf_osRcTFl4A::__concatcp_inner!(CONCATP_NHPMWYD3NJA) }))) } } //////////////////////////////////////////////////////////////////////////////// pub(crate) fn formatc_if_macro_impl(value: FormatIfArgs) -> Result { formatc_macro_impl(value.inner) } //////////////////////////////////////////////////////////////////////////////// pub(crate) fn formatc_macro_impl(fmt_args: FormatArgs) -> Result { let locals = fmt_args.local_variables.iter().map(|arg| &arg.ident); let expr = fmt_args.local_variables.iter().map(|arg| &arg.expr); let strwriter = Ident::new("strwriter", Span::mixed_site()); let writing_formatted = fmt_args .expanded_into .iter() .map(|ei| ei.fmt_call(&strwriter)); let cond_a = fmt_args.condition.iter(); Ok(quote!(({ #[doc(hidden)] #[allow(non_snake_case)] const fn fmt_NHPMWYD3NJA( mut #strwriter: __cf_osRcTFl4A::fmt::Formatter<'_>, ) -> __cf_osRcTFl4A::Result { match (#(&(#expr),)*) { (#(#locals,)*) => { #( __cf_osRcTFl4A::try_!(#writing_formatted); )* }, } __cf_osRcTFl4A::pmr::Ok(()) } __cf_osRcTFl4A::__concatc_inner!( fmt_NHPMWYD3NJA, #((#cond_a) && )* true, ____ ) }))) } pub(crate) fn writec_macro_impl(value: WriteArgs) -> Result { let writer_expr = value.writer_expr; let writer_span = value.writer_span; let FormatArgs { condition: _, expanded_into, local_variables, } = value.format_args; let locals = local_variables.iter().map(|arg| &arg.ident); let expr = local_variables.iter().map(|arg| &arg.expr); let strwriter = Ident::new("strwriter", Span::mixed_site()); let writing_formatted = expanded_into.iter().map(|ei| ei.fmt_call(&strwriter)); let borrow_mutably = quote_spanned!(writer_span=> ((#writer_expr).borrow_mutably())); let make_formatter = quote_spanned!(writer_span => let mut marker = __cf_osRcTFl4A::pmr::IsAWriteMarker::NEW; if false { marker = marker.infer_type(&#strwriter); } let mut #strwriter = marker.coerce(#strwriter); let mut #strwriter = #strwriter.make_formatter(__cf_osRcTFl4A::FormattingFlags::NEW); ); Ok(quote! {({ #[allow(non_snake_case)] match (#borrow_mutably, #(&(#expr),)*) { (#strwriter, #(#locals,)*) => { #make_formatter loop { #( __cf_osRcTFl4A::unwrap_or_else!( #writing_formatted, |e| break __cf_osRcTFl4A::pmr::Err(e) ); )* break __cf_osRcTFl4A::pmr::Ok(()); } } } })}) } const_format_proc_macros-0.2.32/src/format_str/errors.rs000064400000000000000000000063171046102023000216350ustar 00000000000000use proc_macro2::Span; use std::{ fmt::{self, Display}, ops::Range, }; #[derive(Debug, PartialEq)] pub(crate) struct ParseError { pub(crate) pos: usize, pub(crate) kind: ParseErrorKind, } #[derive(Debug, PartialEq)] pub(crate) enum ParseErrorKind { /// A `{` that wasn't closed. UnclosedArg, /// A `}` that doesn't close an argument. InvalidClosedArg, /// When parsing the number of a positional arguments NotANumber { what: String, }, /// When parsing the identifier of a named argument NotAnIdent { what: String, }, UnknownFormatting { what: String, }, } #[allow(dead_code)] impl ParseErrorKind { pub fn not_a_number(what: &str) -> Self { Self::NotANumber { what: what.to_string(), } } pub fn not_an_ident(what: &str) -> Self { Self::NotAnIdent { what: what.to_string(), } } pub fn unknown_formatting(what: &str) -> Self { Self::UnknownFormatting { what: what.to_string(), } } } //////////////////////////////////////////////////////////////////////////////// #[derive(Debug, PartialEq)] pub(crate) struct DisplayParseError<'a> { pub(crate) str: &'a str, pub(crate) error_span: Range, pub(crate) kind: ParseErrorKind, } impl ParseError { fn error_span(&self) -> Range { let len = match &self.kind { ParseErrorKind::UnclosedArg => 0, ParseErrorKind::InvalidClosedArg => 0, ParseErrorKind::NotANumber { what } => what.len(), ParseErrorKind::NotAnIdent { what } => what.len(), ParseErrorKind::UnknownFormatting { what } => what.len(), }; self.pos..self.pos + len } pub(crate) fn into_crate_err(self, span: Span, original_str: &str) -> crate::Error { let display = DisplayParseError { str: original_str, error_span: self.error_span(), kind: self.kind, }; crate::Error::new(span, display) } } impl Display for ParseErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ParseErrorKind::UnclosedArg => f.write_str("unclosed argument"), ParseErrorKind::InvalidClosedArg => f.write_str("`}` closing a nonexistent argument"), ParseErrorKind::NotANumber { what } => writeln!(f, "not a number: \"{}\"", what), ParseErrorKind::NotAnIdent { what } => { writeln!(f, "not a valid identifier: \"{}\"", what) } ParseErrorKind::UnknownFormatting { what } => { writeln!(f, "unknown formatting: \"{}\"", what) } } } } impl Display for DisplayParseError<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("failed to parse the format string ")?; // Gets the amount of chars up to the error, // this is good enough for most cases, // but doesn't acount for multi-char characters. let chars = self.str[..self.error_span.start].chars().count(); writeln!(f, "at the character number {}, ", chars)?; Display::fmt(&self.kind, f)?; Ok(()) } } const_format_proc_macros-0.2.32/src/format_str/parsing.rs000064400000000000000000000164231046102023000217630ustar 00000000000000use super::{FmtArg, FmtStrComponent, FormatStr, ParseError, ParseErrorKind, WhichArg}; use crate::{ formatting::{FormattingFlags, IsAlternate, NumberFormatting}, parse_utils::StrRawness, }; #[cfg(test)] impl FmtStrComponent { pub(super) fn str(s: &str) -> Self { Self::Str(s.to_string(), StrRawness::dummy()) } pub(super) fn arg(which_arg: WhichArg, formatting: FormattingFlags) -> Self { Self::Arg(FmtArg { which_arg, formatting, rawness: StrRawness::dummy(), }) } } impl FmtArg { fn new(which_arg: WhichArg, formatting: FormattingFlags, rawness: StrRawness) -> Self { Self { which_arg, formatting, rawness, } } } #[allow(dead_code)] impl WhichArg { pub(super) fn ident(s: &str) -> Self { Self::Ident(s.to_string()) } } ///////////////////////////////////// #[cfg(test)] impl std::str::FromStr for FormatStr { type Err = ParseError; fn from_str(input: &str) -> Result { parse_format_str(input, StrRawness::dummy()) } } impl FormatStr { pub fn parse(input: &str, rawness: StrRawness) -> Result { parse_format_str(input, rawness) } } fn parse_format_str(input: &str, rawness: StrRawness) -> Result { let mut components = Vec::::new(); let mut arg_start = 0; loop { let open_pos = input.find_from('{', arg_start); let str = &input[arg_start..open_pos.unwrap_or(input.len())]; components.push_arg_str(parse_mid_str(str, arg_start)?, rawness); if let Some(open_pos) = open_pos { let after_open = open_pos + 1; if input[after_open..].starts_with('{') { components.push_arg_str("{".to_string(), rawness); arg_start = open_pos + 2; } else if let Some(close_pos) = input.find_from('}', after_open) { let after_close = close_pos + 1; let arg = parse_fmt_arg(&input[after_open..close_pos], after_open, rawness)?; components.push(FmtStrComponent::Arg(arg)); arg_start = after_close; } else { return Err(ParseError { pos: open_pos, kind: ParseErrorKind::UnclosedArg, }); } } else { break; } } Ok(FormatStr { list: components }) } /// Parses the text between arguments, to unescape `}}` into `}` fn parse_mid_str(str: &str, starts_at: usize) -> Result { let mut buffer = String::with_capacity(str.len()); let mut starts_pos = 0; let bytes = str.as_bytes(); while let Some(close_pos) = str.find_from('}', starts_pos) { let after_close = close_pos + 1; if bytes.get(after_close) == Some(&b'}') { buffer.push_str(&str[starts_pos..after_close]); starts_pos = after_close + 1; } else { return Err(ParseError { pos: starts_at + close_pos, kind: ParseErrorKind::InvalidClosedArg, }); } } buffer.push_str(&str[starts_pos..]); Ok(buffer) } /// Parses the format arguments (`{:?}`, `{foo:}`, `{0}`, etc). /// /// `starts_at` is the offset of `input` in the formatting string. fn parse_fmt_arg(input: &str, starts_at: usize, rawness: StrRawness) -> Result { let colon = input.find(':'); let which_arg_str = &input[..colon.unwrap_or(input.len())]; let formatting_str = colon.map_or("", |x| &input[x + 1..]); let formatting_starts_at = colon.map_or(input.len(), |x| starts_at + x + 1); Ok(FmtArg::new( parse_which_arg(which_arg_str, starts_at)?, parse_formatting(formatting_str, formatting_starts_at)?, rawness, )) } /// Parses the name of the argument in `{foo}`, `{}`, `{bar:?}` /// /// `starts_at` is the offset of `input` in the formatting string. fn parse_which_arg(input: &str, starts_at: usize) -> Result { if input.is_empty() { Ok(WhichArg::Positional(None)) } else if input.as_bytes()[0].is_ascii_digit() { match input.parse::() { Ok(number) => Ok(WhichArg::Positional(Some(number))), Err(_) => Err(ParseError { pos: starts_at, kind: ParseErrorKind::NotANumber { what: input.to_string(), }, }), } } else { parse_ident(input, starts_at) } } /// Parses the `?` and other formatters inside formatting arguments (`{}`). /// /// `starts_at` is the offset of `input` in the formatting string. fn parse_formatting(input: &str, starts_at: usize) -> Result { match input { "#" => return Ok(FormattingFlags::display(IsAlternate::Yes)), "" => return Ok(FormattingFlags::display(IsAlternate::No)), _ => {} } let mut bytes = input.as_bytes(); let make_error = || ParseError { pos: starts_at, kind: ParseErrorKind::UnknownFormatting { what: input.to_string(), }, }; if let [before @ .., b'?'] = bytes { bytes = before; } let mut num_fmt = NumberFormatting::Decimal; let mut is_alternate = IsAlternate::No; for byte in bytes { match byte { b'b' if num_fmt.is_regular() => num_fmt = NumberFormatting::Binary, b'x' if num_fmt.is_regular() => num_fmt = NumberFormatting::LowerHexadecimal, b'X' if num_fmt.is_regular() => num_fmt = NumberFormatting::Hexadecimal, b'#' => is_alternate = IsAlternate::Yes, _ => return Err(make_error()), } } Ok(FormattingFlags::debug(num_fmt, is_alternate)) } /// Parses an identifier in a formatting argument. /// /// `starts_at` is the offset of `input` in the formatting string. fn parse_ident(ident_str: &str, starts_at: usize) -> Result { if is_ident(ident_str) { Ok(WhichArg::Ident(ident_str.to_string())) } else { Err(ParseError { pos: starts_at, kind: ParseErrorKind::NotAnIdent { what: ident_str.to_string(), }, }) } } //////////////////////////////////////////////////////////////////////////////// fn is_ident(s: &str) -> bool { use unicode_xid::UnicodeXID; if s.is_empty() || s == "_" { return false; } let mut chars = s.chars(); let first = chars.next().unwrap(); // For some reason '_' is not considered a valid character for the stard of an ident (first.is_xid_start() || first == '_') && chars.all(|c| c.is_xid_continue()) } //////////////////////////////////////////////////////////////////////////////// trait VecExt { fn push_arg_str(&mut self, str: String, rawness: StrRawness); } impl VecExt for Vec { fn push_arg_str(&mut self, str: String, rawness: StrRawness) { if !str.is_empty() { self.push(FmtStrComponent::Str(str, rawness)); } } } trait StrExt { fn find_from(&self, c: char, from: usize) -> Option; } impl StrExt for str { fn find_from(&self, c: char, from: usize) -> Option { self[from..].find(c).map(|p| p + from) } } const_format_proc_macros-0.2.32/src/format_str/tests.rs000064400000000000000000000150341046102023000214570ustar 00000000000000use super::*; use super::{ParseError as PE, ParseErrorKind as PEK}; use crate::formatting::{FormattingFlags as FF, IsAlternate, NumberFormatting}; use fastrand::Rng; use std::ops::RangeInclusive; fn err(s: &'static str) -> ParseError { FormatStr::parse(s, StrRawness::dummy()).unwrap_err() } fn ok(s: &'static str) -> FormatStr { FormatStr::parse(s, StrRawness::dummy()).unwrap() } const NOALT: IsAlternate = IsAlternate::No; const NFDEC: NumberFormatting = NumberFormatting::Decimal; #[test] fn unclosed_arg() { assert_eq!( err("_{"), PE { pos: 1, kind: PEK::UnclosedArg } ); assert_eq!( err("___{"), PE { pos: 3, kind: PEK::UnclosedArg } ); assert_eq!( err("___{__"), PE { pos: 3, kind: PEK::UnclosedArg } ); } #[test] fn invalid_closed_arg() { assert_eq!( err("_}"), PE { pos: 1, kind: PEK::InvalidClosedArg } ); assert_eq!( err("___}"), PE { pos: 3, kind: PEK::InvalidClosedArg } ); assert_eq!( err("___}__"), PE { pos: 3, kind: PEK::InvalidClosedArg } ); } #[test] fn not_a_number() { assert_eq!( err(" {0a} "), PE { pos: 3, kind: PEK::not_a_number("0a") } ); assert_eq!( err(" {4B:?} "), PE { pos: 4, kind: PEK::not_a_number("4B") } ); assert_eq!( err(" {6_:} "), PE { pos: 5, kind: PEK::not_a_number("6_") } ); } #[test] fn not_an_ident() { assert_eq!( err(" {?} "), PE { pos: 3, kind: PEK::not_an_ident("?") } ); assert_eq!( err(" {_} "), PE { pos: 3, kind: PEK::not_an_ident("_") } ); assert_eq!( err(" {a?} "), PE { pos: 4, kind: PEK::not_an_ident("a?") } ); assert_eq!( err(" {_?:} "), PE { pos: 5, kind: PEK::not_an_ident("_?") } ); } #[test] fn unknown_formatting() { assert_eq!( err(" {:!} "), PE { pos: 5, kind: PEK::unknown_formatting("!") } ); assert_eq!( err(" {:????} "), PE { pos: 6, kind: PEK::unknown_formatting("????") } ); } #[test] fn ok_cases() { assert_eq!( ok("{{{100}}}{200:}{300:?}").list, vec![ FmtStrComponent::str("{"), FmtStrComponent::arg(WhichArg::Positional(Some(100)), FF::display(NOALT)), FmtStrComponent::str("}"), FmtStrComponent::arg(WhichArg::Positional(Some(200)), FF::display(NOALT)), FmtStrComponent::arg(WhichArg::Positional(Some(300)), FF::debug(NFDEC, NOALT)), ] ); assert_eq!( ok("{{{}}}{:}{:?}").list, vec![ FmtStrComponent::str("{"), FmtStrComponent::arg(WhichArg::Positional(None), FF::display(NOALT)), FmtStrComponent::str("}"), FmtStrComponent::arg(WhichArg::Positional(None), FF::display(NOALT)), FmtStrComponent::arg(WhichArg::Positional(None), FF::debug(NFDEC, NOALT)), ] ); assert_eq!( ok("{{{AA}}}{BB:}{CC:?}").list, vec![ FmtStrComponent::str("{"), FmtStrComponent::arg(WhichArg::ident("AA"), FF::display(NOALT)), FmtStrComponent::str("}"), FmtStrComponent::arg(WhichArg::ident("BB"), FF::display(NOALT)), FmtStrComponent::arg(WhichArg::ident("CC"), FF::debug(NFDEC, NOALT)), ] ); assert_eq!( ok("AA {BB} CC {__:}{_aA0ñÑóö:}{FF:?} EE").list, vec![ FmtStrComponent::str("AA "), FmtStrComponent::arg(WhichArg::ident("BB"), FF::display(NOALT)), FmtStrComponent::str(" CC "), FmtStrComponent::arg(WhichArg::ident("__"), FF::display(NOALT)), FmtStrComponent::arg(WhichArg::ident("_aA0ñÑóö"), FF::display(NOALT)), FmtStrComponent::arg(WhichArg::ident("FF"), FF::debug(NFDEC, NOALT)), FmtStrComponent::str(" EE"), ] ); } //////////////////////////////////////////////////////////////////////////////// trait RngExt { /// # Panic /// /// Panics if the input slice is empty fn pick<'a, T>(&self, slice: &'a [T]) -> &'a T; /// # Panic /// /// Panics if there are no `chars` in the `bounds` fn char_(&self, bounds: RangeInclusive) -> char; } impl RngExt for Rng { fn pick<'a, T>(&self, slice: &'a [T]) -> &'a T { &slice[self.usize(0..slice.len())] } fn char_(&self, bounds: RangeInclusive) -> char { if let None = bounds.clone().next() { panic!("There are no chars in the {:?} bounds", bounds); } let u32_bounds = u32::from(*bounds.start())..=u32::from(*bounds.end()); loop { if let Some(x) = std::char::from_u32(self.u32(u32_bounds.clone())) { break x; } } } } fn generate_input() -> String { let rng = Rng::new(); let len = rng.usize(0..40); let mut string = String::with_capacity(len * 2); for _ in 0..len { match rng.u8(0..100) { 0..=10 => string.push(*rng.pick(&['{', '}'])), _ => { let range = rng .pick(&[ 'A'..='Z', 'a'..='z', '0'..='9', '\u{0000}'..='\u{007F}', '\u{007F}'..='\u{07FF}', '\u{07FF}'..='\u{FFFF}', '\u{10000}'..='\u{10FFFF}', ]) .clone(); for _ in 0..rng.u32(0..4) { string.push(rng.char_(range.clone())); } } }; } string } #[test] fn never_panics() { let iters = 200; loop { let mut ok_count = 0u64; for _ in 0..iters { let input = generate_input(); // println!("input: {}\n\n", input); if input.parse::().is_ok() { ok_count += 1; } } if ok_count * 100 / iters > 30 { break; } else { println!("retrying never_panics"); } } } const_format_proc_macros-0.2.32/src/format_str.rs000064400000000000000000000013311046102023000203100ustar 00000000000000use crate::{formatting::FormattingFlags, parse_utils::StrRawness}; mod errors; mod parsing; #[cfg(test)] mod tests; pub(crate) use self::errors::{ParseError, ParseErrorKind}; #[derive(Debug, PartialEq)] pub(crate) struct FormatStr { pub(crate) list: Vec, } #[derive(Debug, PartialEq)] pub(crate) enum FmtStrComponent { Str(String, StrRawness), Arg(FmtArg), } /// An argument in the format string eg: `"{foo:?}"` #[derive(Debug, PartialEq)] pub(crate) struct FmtArg { pub(crate) which_arg: WhichArg, pub(crate) formatting: FormattingFlags, pub(crate) rawness: StrRawness, } #[derive(Debug, PartialEq)] pub(crate) enum WhichArg { Ident(String), Positional(Option), } const_format_proc_macros-0.2.32/src/formatting.rs000064400000000000000000000073771046102023000203220ustar 00000000000000use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens, TokenStreamExt}; #[derive(Debug, Copy, Clone, PartialEq)] pub(crate) enum Formatting { Debug(NumberFormatting), Display, } #[derive(Debug, Copy, Clone, PartialEq)] pub(crate) enum NumberFormatting { Decimal, Hexadecimal, LowerHexadecimal, Binary, } impl NumberFormatting { pub(crate) fn is_regular(self) -> bool { matches!(self, NumberFormatting::Decimal) } } impl ToTokens for NumberFormatting { fn to_tokens(&self, ts: &mut TokenStream2) { ts.append_all(match self { Self::Decimal => return, Self::Hexadecimal => quote!(.set_hexadecimal()), Self::LowerHexadecimal => quote!(.set_lower_hexadecimal()), Self::Binary => quote!(.set_binary()), }); } } //////////////////////////////////////////////////////////////////////////////// #[derive(Debug, Copy, Clone, PartialEq)] pub(crate) enum IsAlternate { Yes, No, } //////////////////////////////////////////////////////////////////////////////// #[derive(Debug, Copy, Clone, PartialEq)] pub(crate) struct FormattingFlags { pub(crate) formatting: Formatting, pub(crate) is_alternate: IsAlternate, } impl FormattingFlags { #[inline] pub(crate) const fn display(is_alternate: IsAlternate) -> Self { Self { formatting: Formatting::Display, is_alternate, } } #[inline] pub(crate) const fn debug(num_fmt: NumberFormatting, is_alternate: IsAlternate) -> Self { Self { formatting: Formatting::Debug(num_fmt), is_alternate, } } } impl FormattingFlags { pub(crate) fn to_pargument_method_name(self) -> Ident { let name = match self.formatting { Formatting::Display => "to_pargument_display", Formatting::Debug { .. } => "to_pargument_debug", }; Ident::new(name, Span::mixed_site()) } #[allow(dead_code)] pub(crate) fn fmt_method_name(self) -> Ident { let name = match self.formatting { Formatting::Display => "const_display_fmt", Formatting::Debug { .. } => "const_debug_fmt", }; Ident::new(name, Span::mixed_site()) } #[allow(dead_code)] pub(crate) fn len_method_name(self) -> Ident { let name = match self.formatting { Formatting::Display => "const_display_fmt", Formatting::Debug { .. } => "const_debug_fmt", }; Ident::new(name, Span::mixed_site()) } } impl ToTokens for FormattingFlags { fn to_tokens(&self, ts: &mut TokenStream2) { use self::{IsAlternate as IA, NumberFormatting as FM}; let formatting = match self.formatting { Formatting::Display => NumberFormatting::Decimal, Formatting::Debug(num_fmt) => num_fmt, }; ts.append_all(match (self.is_alternate, formatting) { (IA::No, FM::Decimal) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__REG), (IA::No, FM::Hexadecimal) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__HEX), (IA::No, FM::LowerHexadecimal) => { quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__LOWHEX) } (IA::No, FM::Binary) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__BIN), (IA::Yes, FM::Decimal) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__A_REG), (IA::Yes, FM::Hexadecimal) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__A_HEX), (IA::Yes, FM::LowerHexadecimal) => { quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__A_LOWHEX) } (IA::Yes, FM::Binary) => quote!(__cf_osRcTFl4A::pmr::FormattingFlags::__A_BIN), }); } } const_format_proc_macros-0.2.32/src/lib.rs000064400000000000000000000063721046102023000167100ustar 00000000000000#![allow(clippy::or_fun_call)] #![allow(clippy::derive_partial_eq_without_eq)] use proc_macro::TokenStream as TokenStream1; use proc_macro2::TokenStream as TokenStream2; #[cfg(feature = "derive")] #[macro_use] mod macros; #[cfg(feature = "derive")] mod datastructure; mod error; #[cfg(feature = "derive")] mod derive_debug; mod format_args; mod format_str; mod format_macro; mod formatting; mod parse_utils; mod respan_to_macro; mod shared_arg_parsing; mod spanned; mod utils; #[cfg(test)] mod test_utils; use crate::error::Error; use crate::parse_utils::MyParse; fn compile_err_empty_str(e: crate::Error) -> TokenStream2 { let e = e.to_compile_error(); quote::quote!({ #e; "" }) } #[doc(hidden)] #[proc_macro] pub fn __concatcp_impl(input: TokenStream1) -> TokenStream1 { MyParse::parse_token_stream_1(input) .and_then(format_macro::concatcp_impl) .unwrap_or_else(compile_err_empty_str) .into() } /// Input syntax: `"format string", (arg0), (name = arg1)` (with optional trailing comma). /// /// The arguments are parenthesized to not require syn to parse `arg0` and `arg1` as syn::Expr, /// they're just parsed as a `TokenStream2`. /// /// They're guaranteed to be expressions when this macro is invoked by `const_format` macros, /// which should be the only ones to do so. #[doc(hidden)] #[proc_macro] pub fn __formatcp_impl(input: TokenStream1) -> TokenStream1 { MyParse::parse_token_stream_1(input) .and_then(format_macro::formatcp_impl) .unwrap_or_else(compile_err_empty_str) .into() } #[doc(hidden)] #[proc_macro] pub fn __formatc_impl(input: TokenStream1) -> TokenStream1 { MyParse::parse_token_stream_1(input) .and_then(format_macro::formatc_macro_impl) .unwrap_or_else(compile_err_empty_str) .into() } #[doc(hidden)] #[proc_macro] pub fn __formatc_if_impl(input: TokenStream1) -> TokenStream1 { MyParse::parse_token_stream_1(input) .and_then(format_macro::formatc_if_macro_impl) .unwrap_or_else(compile_err_empty_str) .into() } #[doc(hidden)] #[proc_macro] pub fn __formatcp_if_impl(input: TokenStream1) -> TokenStream1 { MyParse::parse_token_stream_1(input) .and_then(format_macro::formatcp_if_macro_impl) .unwrap_or_else(compile_err_empty_str) .into() } #[doc(hidden)] #[proc_macro] pub fn __writec_impl(input: TokenStream1) -> TokenStream1 { MyParse::parse_token_stream_1(input) .and_then(format_macro::writec_macro_impl) .unwrap_or_else(|e| { let e = e.to_compile_error(); quote::quote!({ #e; ::core::result::Result::Ok(()) }) }) .into() } #[cfg(feature = "derive")] #[proc_macro_derive(ConstDebug, attributes(cdeb))] pub fn derive_const_debug(input: TokenStream1) -> TokenStream1 { syn::parse(input) .map_err(crate::Error::from) .and_then(derive_debug::derive_constdebug_impl) .unwrap_or_else(|e| e.to_compile_error()) .into() } /// `__respan_to!(( foo tokens ) bar tokens )` /// Respan all the bar tokens to the span of the foo tokens #[proc_macro] pub fn respan_to(input: TokenStream1) -> TokenStream1 { crate::respan_to_macro::implementation(input.into()).into() } const_format_proc_macros-0.2.32/src/macros.rs000064400000000000000000000017061046102023000174220ustar 00000000000000#![allow(unused_macros)] #[doc(hidden)] macro_rules! to_stream { ( $stream:ident ; $($expr:expr),* $(,)* ) => {{ // use quote::TokenStreamExt; $( $expr.to_tokens($stream); )* }} } #[doc(hidden)] macro_rules! spanned_err { ( $e:expr, $($fmt:tt)* ) => ({ $crate::utils::spanned_err( &$e, &format!($($fmt)*), ) }) } #[doc(hidden)] macro_rules! return_spanned_err { ( $e:expr, $($fmt:tt)* ) => ({ return Err($crate::utils::spanned_err( &$e, &format!($($fmt)*), )) }) } #[doc(hidden)] macro_rules! syn_err { ( $span:expr, $($fmt:tt)* ) => ({ $crate::utils::syn_err( $span, &format!($($fmt)*), ) }) } #[doc(hidden)] macro_rules! return_syn_err { ( $span:expr, $($fmt:tt)* ) => ({ return Err($crate::utils::syn_err( $span, &format!($($fmt)*), )) }) } const_format_proc_macros-0.2.32/src/parse_utils.rs000064400000000000000000000253471046102023000204770ustar 00000000000000use crate::{spanned::Spans, utils::Peekable2, Error}; use proc_macro2::{ token_stream::IntoIter, Delimiter, Group, Ident, Punct, Span, TokenStream as TokenStream2, TokenTree as TokenTree2, }; use std::{cmp::PartialEq, ops::Range}; pub type ParseStream<'a> = &'a mut ParseBuffer; pub struct ParseBuffer { iter: Peekable2, } impl ParseBuffer { pub fn new(ts: TokenStream2) -> Self { let iter = Peekable2::new(ts); Self { iter } } pub fn is_empty(&mut self) -> bool { self.iter.is_empty() } pub fn peek(&mut self) -> Option<&TokenTree2> { self.iter.peek() } pub fn peek2(&mut self) -> Option<&TokenTree2> { self.iter.peek2() } pub fn parse_punct(&mut self, c: char) -> Result { match self.next() { Some(TokenTree2::Punct(x)) if x.as_char() == c => Ok(x), Some(x) => Err(Error::new(x.span(), &format!("Expected a '{}' token", c))), None => Err(Error::new( Span::mixed_site(), &format!("Expected a '{}' token", c), )), } } pub fn parse_opt_punct(&mut self, c: char) -> Result, crate::Error> { match self.next() { Some(TokenTree2::Punct(x)) if x.as_char() == c => Ok(Some(x)), Some(x) => Err(Error::new(x.span(), &format!("Expected a '{}' token", c))), None => Ok(None), } } pub fn parse_ident(&mut self) -> Result { match self.next() { Some(TokenTree2::Ident(x)) => Ok(x), Some(x) => Err(Error::new(x.span(), "Expected an identifier")), None => Err(Error::new(Span::mixed_site(), "Expected an identifier")), } } pub fn parse_paren(&mut self) -> Result { match self.next() { Some(TokenTree2::Group(group)) if group.delimiter() == Delimiter::Parenthesis => { Ok(Parentheses { paren_span: group.span(), contents: group.stream(), }) } Some(x) => Err(Error::new( x.span(), &format!("Expected parentheses: found {}", x), )), None => Err(Error::new( Span::mixed_site(), "Expected parentheses, found nothing", )), } } pub fn parse_unwrap_paren(&mut self, f: F) -> Result where F: FnOnce(ParseStream<'_>) -> Result, { if matches!(self.peek(), Some(TokenTree2::Group(x)) if x.delimiter() == Delimiter::Parenthesis ) { if let Some(TokenTree2::Group(group)) = self.next() { ParseBuffer::new(group.stream()).parse_unwrap_tt(f) } else { unreachable!("But I peeked for a Parenthesis delimited TokenTree::Group!!") } } else { f(self) } } pub fn parse_unwrap_group(&mut self, f: F) -> Result where F: FnOnce(ParseStream<'_>) -> Result, { if let Some(TokenTree2::Group(group)) = self.next() { ParseBuffer::new(group.stream()).parse_unwrap_tt(f) } else { f(self) } } pub fn parse_token_stream_and_span(&mut self) -> (TokenStream2, Spans) { let mut start = match self.peek() { Some(x) => x.span(), None => Span::call_site(), }; let mut end = start; let ts = self .inspect(|tt| { end = tt.span(); if let Some(next) = start.join(end) { start = next; } }) .collect::(); (ts, Spans { start, end }) } /// Unwraps a none-delimited token tree to parse a type, /// if the first token is not a none-delimited token tree it parses the type in /// the passed in ParseStream. pub fn parse_unwrap_tt(&mut self, f: F) -> Result where F: FnOnce(ParseStream<'_>) -> Result, { if matches!(self.peek(), Some(TokenTree2::Group(x)) if x.delimiter() == Delimiter::None ) { if let Some(TokenTree2::Group(group)) = self.next() { ParseBuffer::new(group.stream()).parse_unwrap_tt(f) } else { unreachable!("But I peeked for a None delimited TokenTree::Group!!") } } else { f(self) } } } impl Iterator for ParseBuffer { type Item = TokenTree2; fn next(&mut self) -> Option { self.iter.next() } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } /////////////////////////////////////////////////////////////////////////////// pub struct Parentheses { #[allow(dead_code)] pub paren_span: Span, pub contents: TokenStream2, } /////////////////////////////////////////////////////////////////////////////// pub struct LitStr { value: String, pub rawness: StrRawness, pub inside_lit: Range, pub span: Span, } impl LitStr { pub fn value(&self) -> &str { &self.value[self.inside_lit.clone()] } pub(crate) fn parse_from_literal(literal: &proc_macro2::Literal) -> Result { let mut value = literal.to_string(); // Ignoring the quote characters let mut range = 1..value.len() - 1; let span = literal.span(); let is_raw = if let Some(suffix) = value.strip_prefix('r') { let hashes = suffix.bytes().take_while(|x| *x == b'#').count(); if value.as_bytes()[1 + hashes] != b'"' { return Err(Error::new( span, &format!("Expected a string literal, found: {}", literal), )); } // Ignoring the r and hashes range.start += 1 + hashes; range.end -= hashes; Some(hashes as u32) } else { let mut matches = value.match_indices(r#"\u"#).peekable(); if matches.peek().is_some() { let mut prev_end = 0; let mut new = String::with_capacity(value.len()); for (pos, _) in matches { new.push_str(&value[prev_end..pos]); let past_open = pos + 3; let off_close = value[pos..].find('}').unwrap(); let c = &value[past_open..pos + off_close]; let c = u32::from_str_radix(c, 16).unwrap(); let c = std::char::from_u32(c).unwrap(); // if matches!(c, '\\' | '"') { // new.push('\\'); // } new.push(c); prev_end = pos + off_close + 1; } new.push_str(&value[prev_end..]); value = new; } range = 1..value.len() - 1; None }; Ok(Self { value, rawness: StrRawness { is_raw, span }, inside_lit: range, span, }) } } #[derive(Debug, Copy, Clone)] pub struct StrRawness { is_raw: Option, span: Span, } impl PartialEq for StrRawness { fn eq(&self, other: &Self) -> bool { self.is_raw == other.is_raw } } impl StrRawness { #[cfg(test)] pub fn dummy() -> Self { Self { is_raw: Some(4), span: Span::mixed_site(), } } pub fn span(&self) -> Span { self.span } /// Tokenizes a slice of the parsed string literal. pub fn tokenize_sub(&self, str: &str) -> TokenStream2 { let mut buffer = String::new(); match self.is_raw { Some(hashes) => { let hashes = hashes as usize; buffer.reserve(3 + hashes + str.len() + hashes); buffer.push('r'); let hashes = (0..hashes).map(|_| '#'); buffer.extend(hashes.clone()); buffer.push('"'); buffer.push_str(str); buffer.push('"'); buffer.extend(hashes); } None => { buffer.reserve(2 + str.len()); buffer.push('"'); buffer.push_str(str); buffer.push('"'); } } buffer .parse::() .unwrap() .set_span_recursive(self.span) } } /////////////////////////////////////////////////////////////////////////////// pub trait TokenTreeExt: Sized { fn as_token_tree(&self) -> &TokenTree2; fn into_token_tree(self) -> TokenTree2; fn is_punct(&self, c: char) -> bool { matches!(self.as_token_tree(), TokenTree2::Punct(p) if p.as_char() == c) } fn is_paren(&self) -> bool { matches!( self.as_token_tree(), TokenTree2::Group(g) if g.delimiter() == Delimiter::Parenthesis ) } fn is_ident(&self, ident: &str) -> bool { matches!(self.as_token_tree(), TokenTree2::Ident(x) if x == ident) } fn set_span_recursive(self, span: Span) -> TokenTree2 { let mut tt = self.into_token_tree(); tt.set_span(span); if let TokenTree2::Group(group) = tt { let delim = group.delimiter(); let stream = group.stream().set_span_recursive(span); tt = TokenTree2::Group(Group::new(delim, stream)); } tt.set_span(span); tt } } impl TokenTreeExt for TokenTree2 { fn as_token_tree(&self) -> &TokenTree2 { self } fn into_token_tree(self) -> TokenTree2 { self } } /////////////////////////////////////////////////////////////////////////////// pub trait TokenStream2Ext: Sized { fn into_token_stream(self) -> TokenStream2; fn set_span_recursive(self, span: Span) -> TokenStream2 { self.into_token_stream() .into_iter() .map(|tt| tt.set_span_recursive(span)) .collect() } } impl TokenStream2Ext for TokenStream2 { fn into_token_stream(self) -> TokenStream2 { self } } /////////////////////////////////////////////////////////////////////////////// pub trait MyParse: Sized { fn parse(input: ParseStream<'_>) -> Result; fn parse_token_stream_1(input: proc_macro::TokenStream) -> Result { Self::parse(&mut ParseBuffer::new(TokenStream2::from(input))) } fn parse_token_stream_2(input: TokenStream2) -> Result { Self::parse(&mut ParseBuffer::new(input)) } } /////////////////////////////////////////////////////////////////////////////// const_format_proc_macros-0.2.32/src/respan_to_macro.rs000064400000000000000000000021601046102023000213040ustar 00000000000000use crate::parse_utils::TokenTreeExt; use proc_macro2::{Delimiter, Span, TokenStream as TokenStream2, TokenTree as TokenTree2}; const MSG: &str = "Expected the macro to be called as `respan_to!((tokens) more tokens)`"; fn parse_paren(tt: TokenTree2) -> TokenStream2 { match tt { TokenTree2::Group(group) if group.delimiter() == Delimiter::Parenthesis => group.stream(), _ => panic!("{}", MSG), } } fn get_span(ts: TokenStream2) -> Span { let mut iter = ts.into_iter(); match iter.next() { Some(TokenTree2::Group(group)) if group.delimiter() == Delimiter::None => { get_span(group.stream()) } Some(first_tt) => { let mut span = first_tt.span(); for tt in iter { span = span.join(tt.span()).unwrap_or(span); } span } None => Span::mixed_site(), } } pub(crate) fn implementation(ts: TokenStream2) -> TokenStream2 { let mut iter = ts.into_iter(); let span_to = get_span(parse_paren(iter.next().expect(MSG))); iter.map(|tt| tt.set_span_recursive(span_to)).collect() } const_format_proc_macros-0.2.32/src/shared_arg_parsing.rs000064400000000000000000000031511046102023000217540ustar 00000000000000//! Types for parsing arguments, shared by many of the macros use crate::{ parse_utils::{MyParse, ParseBuffer, ParseStream}, spanned::Spans, }; use proc_macro2::TokenStream as TokenStream2; use quote::ToTokens; //////////////////////////////////////////////// // An expression inside `(...)` pub(crate) struct ExprArg { pub(crate) span: Spans, /// Using a TokenStream2 because it is validated to be a valid expression in /// the macro_rules! macros that call these proc macros. pub(crate) expr: TokenStream2, } impl ToTokens for ExprArg { fn to_tokens(&self, ts: &mut TokenStream2) { self.expr.to_tokens(ts); } } /// A sequence of comma separated expressions wrapped in parentheses (with a trailing comma) pub(crate) struct ExprArgs { pub(crate) args: Vec, } //////////////////////////////////////////////// impl MyParse for ExprArg { fn parse(input: ParseStream<'_>) -> Result { let paren = input.parse_paren()?; let mut content = ParseBuffer::new(paren.contents); content.parse_unwrap_tt(|content| { let (expr, span) = content.parse_token_stream_and_span(); Ok(Self { span, expr }) }) } } //////////////////////////////////////////////// impl MyParse for ExprArgs { fn parse(input: ParseStream<'_>) -> Result { let mut args = Vec::new(); while !input.is_empty() { args.push(ExprArg::parse(input)?); if !input.is_empty() { input.parse_punct(',')?; } } Ok(Self { args }) } } const_format_proc_macros-0.2.32/src/spanned.rs000064400000000000000000000003341046102023000175620ustar 00000000000000use proc_macro2::Span; #[derive(Copy, Clone)] pub struct Spans { pub start: Span, pub end: Span, } impl Spans { pub fn joined(self) -> Span { self.start.join(self.end).unwrap_or(self.start) } } const_format_proc_macros-0.2.32/src/test_utils.rs000064400000000000000000000014441046102023000203340ustar 00000000000000pub trait StrExt { fn as_str(&self) -> &str; /// Checks that these needles exist consequtively in self. /// /// Example: `"hello world".consecutive_in_set(&["he", "wor"])` returns `true`. /// Example: `"hello world".consecutive_in_set(&["wor", "he"])` returns `false`. fn consecutive_in_self(&self, needles: &[&str]) -> bool { let mut rem = self.as_str(); for needle in needles { rem = match rem.find(needle) { Some(next) => &rem[next + needle.len()..], None => return false, }; } true } } impl StrExt for str { #[inline(always)] fn as_str(&self) -> &str { self } } impl StrExt for String { #[inline(always)] fn as_str(&self) -> &str { self } } const_format_proc_macros-0.2.32/src/utils.rs000064400000000000000000000076421046102023000173030ustar 00000000000000use proc_macro2::Span; #[cfg(feature = "derive")] use quote::ToTokens; use std::{ collections::VecDeque, iter::Fuse, mem, ops::{Deref, DerefMut}, }; pub(crate) fn dummy_ident() -> proc_macro2::Ident { proc_macro2::Ident::new("__dummy__", Span::mixed_site()) } //////////////////////////////////////////////////////////////////////////////////// #[cfg(feature = "derive")] pub fn spanned_err(tokens: &dyn ToTokens, display: &dyn std::fmt::Display) -> crate::Error { use syn::spanned::Spanned; crate::Error::new(tokens.span(), display) } //////////////////////////////////////////////////////////////////////////////////// #[derive(Clone, Debug)] pub struct Peekable2 { iter: Fuse, queue: VecDeque, } impl Peekable2> { #[allow(clippy::new_ret_no_self)] pub fn new(iter: I) -> Peekable2 { Peekable2 { iter: iter.into_iter().fuse(), queue: VecDeque::new(), } } } impl Peekable2 { pub fn is_empty(&mut self) -> bool { self.peek().is_none() } pub fn peek(&mut self) -> Option<&I::Item> { if self.queue.is_empty() { self.queue.push_back(self.iter.next()?); } Some(&self.queue[0]) } pub fn peek2(&mut self) -> Option<&I::Item> { while self.queue.len() < 2 { self.queue.push_back(self.iter.next()?); } Some(&self.queue[1]) } } impl Iterator for Peekable2 where I: Iterator, { type Item = I::Item; fn next(&mut self) -> Option { if let opt @ Some(_) = self.queue.pop_front() { opt } else { self.iter.next() } } fn size_hint(&self) -> (usize, Option) { let (low, high) = self.iter.size_hint(); let len = self.queue.len(); (low + len, high.map(|x| x.saturating_add(len))) } } //////////////////////////////////////////////////////////////////////////////////// /// A result wrapper which panics if it's the error variant is not handled, /// by calling `.into_result()`. #[derive(Debug, Clone)] pub struct LinearResult { errors: Result<(), crate::Error>, } impl Drop for LinearResult { fn drop(&mut self) { mem::replace(&mut self.errors, Ok(())).expect("Expected LinearResult to be handled"); } } impl LinearResult { #[inline] pub fn new(res: Result<(), crate::Error>) -> Self { Self { errors: res } } #[inline] pub fn ok() -> Self { Self::new(Ok(())) } } impl From> for LinearResult { #[inline] fn from(res: Result<(), crate::Error>) -> Self { Self::new(res) } } impl Deref for LinearResult { type Target = Result<(), crate::Error>; fn deref(&self) -> &Result<(), crate::Error> { &self.errors } } impl DerefMut for LinearResult { fn deref_mut(&mut self) -> &mut Result<(), crate::Error> { &mut self.errors } } #[allow(dead_code)] impl LinearResult { #[inline] pub fn into_result(mut self) -> Result<(), crate::Error> { mem::replace(&mut self.errors, Ok(())) } #[inline] pub fn take(&mut self) -> Result<(), crate::Error> { self.replace(Ok(())) } #[inline] pub fn replace(&mut self, other: Result<(), crate::Error>) -> Result<(), crate::Error> { mem::replace(&mut self.errors, other) } #[inline] pub fn push_err(&mut self, err: E) where E: Into, { let err = err.into(); match &mut self.errors { this @ Ok(_) => *this = Err(err), Err(e) => e.combine(err), } } #[inline] pub fn combine_err(&mut self, res: Result<(), E>) where E: Into, { if let Err(err) = res { let err = err.into(); self.push_err(err); } } }