quote-use-macros-0.8.4/.cargo_vcs_info.json0000644000000001560000000000100142600ustar { "git": { "sha1": "05096f346f8b17fba8a57a235b958917ad9d99e0" }, "path_in_vcs": "quote-use-macros" }quote-use-macros-0.8.4/Cargo.toml0000644000000026540000000000100122630ustar # 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" name = "quote-use-macros" version = "0.8.4" build = false include = [ "src/**/*", "LICENSE", "README.md", ] autobins = false autoexamples = false autotests = false autobenches = false description = "Support `use` in procmacros hygienically" documentation = "https://docs.rs/quote-use" readme = "README.md" keywords = ["macro"] categories = [ "rust-patterns", "development-tools::procedural-macro-helpers", "parsing", ] license = "MIT" repository = "https://github.com/ModProg/quote-use" [package.metadata.release] shared-version = true [lib] name = "quote_use_macros" path = "src/lib.rs" proc-macro = true [dependencies.proc-macro-utils] version = "0.10" [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "2" features = [ "parsing", "extra-traits", "clone-impls", "printing", ] default-features = false [dev-dependencies.pretty_assertions] version = "1.4" quote-use-macros-0.8.4/Cargo.toml.orig000064400000000000000000000013431046102023000157360ustar 00000000000000[package] categories = [ "rust-patterns", "development-tools::procedural-macro-helpers", "parsing", ] description = "Support `use` in procmacros hygienically" documentation = "https://docs.rs/quote-use" include = ["src/**/*", "LICENSE", "README.md"] keywords = ["macro"] license = "MIT" readme = "../README.md" repository = "https://github.com/ModProg/quote-use" version = "0.8.4" edition = "2021" name = "quote-use-macros" [lib] proc-macro = true [dependencies] proc-macro-utils = "0.10" proc-macro2 = "1" quote = "1" syn = { version = "2", features = ["parsing", "extra-traits", "clone-impls", "printing"], default-features = false} [dev-dependencies] pretty_assertions = "1.4" [package.metadata.release] shared-version = true quote-use-macros-0.8.4/README.md000064400000000000000000000067011046102023000143310ustar 00000000000000# Use statements in `quote!` [![Crates.io Version](https://img.shields.io/crates/v/quote-use.svg)](https://crates.io/crates/quote-use) [![CI](https://github.com/ModProg/quote-use/actions/workflows/test.yml/badge.svg)](https://github.com/ModProg/quote-use/actions/workflows/test.yml) [![Docs.rs Documentation](https://img.shields.io/docsrs/quote-use)](https://docs.rs/crate/quote-use) ## Description Macro to simplify using Types in the [`quote!`](https://docs.rs/quote/latest/quote/macro.quote.html) macro. ## Usage The [`quote_use!`](https://docs.rs/quote-use/latest/quote_use/macro.quote_use.html) macro can be used just like [`quote!`](https://docs.rs/quote/latest/quote/macro.quote.html), but with the added functionality of adding use statements at the top: ```rust quote_use!{ use std::fs::read; read("src/main.rs") } ``` This will expand to the equivalent statement using [`quote!`](https://docs.rs/quote/latest/quote/macro.quote.html): ```rust quote!{ ::std::fs::read::read("src/main.rs") } ``` ### Prelude This also allows to use contents of the rust prelude directly: ```rust quote_use!{ Some("src/main.rs") } ``` #### Overriding prelude When you want to use your own type instead of the prelude type this can be achieved by simply importing it like so ```rust quote_use!{ use anyhow::Result; Result } ``` #### Different preludes By default [`quote_use!`](https://docs.rs/quote-use/latest/quote_use/macro.quote_use.html) uses the [std prelude](std::prelude) for [2021 edition](std::prelude::rust_2021), but this can be configured via features, and also completely disabled. - **`prelude_std`**: Enables [`std::prelude::v1`](https://doc.rust-lang.org/nightly/std/prelude/v1/index.html) (incompatible with `prelude_core`) - `prelude_core`: Enables [`core::prelude::v1`](https://doc.rust-lang.org/nightly/core/prelude/v1/index.html) (incompatible with `prelude_std`) - **`prelude_2021`**: Enables [`core::prelude::rust_2021`](https://doc.rust-lang.org/nightly/core/prelude/rust_2021/index.html) (requires either `prelude_std` or `prelude_core`) ### Other quote macros There are also variants for other quote macros from [syn](https://docs.rs/syn/latest/syn/) and [quote](https://docs.rs/quote/latest/quote/): - [`quote_use!`](https://docs.rs/quote-use/latest/quote_use/macro.quote_use.html) and [`quote_spanned_use!`](https://docs.rs/quote-use/latest/quote_use/macro.quote_spanned_use.html) as replacement for [`quote!`](https://docs.rs/quote/latest/quote/macro.quote.html) and [`quote_spanned!`](https://docs.rs/quote/latest/quote/macro.quote_spanned.html) respectively - [`parse_quote_use!`](https://docs.rs/quote-use/latest/quote_use/macro.parse_quote_use.html) and [`parse_quote_spanned_use!`](https://docs.rs/quote-use/latest/quote_use/macro.parse_quote_spanned_use.html) for [`parse_quote!`](https://docs.rs/syn/latest/syn/macro.parse_quote.html) and [`parse_quote_spanned!`](https://docs.rs/syn/latest/syn/macro.parse_quote_spanned.html) ## Auto namespacing idents Until [`Span::def_site`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.def_site) is stabilized, identifiers in e.g. let bindings in proc-macro expansions can collide with e.g. constants. To circumvent this you can enable the feature `namespace_idents` which will replace all identifiers with autonamespaced ones using the pattern `"__{crate_name}_{ident}"`. quote-use-macros-0.8.4/src/lib.rs000064400000000000000000000100751046102023000147540ustar 00000000000000//! Proc-macros for [`quote-use`](https://docs.rs/quote-use/). use proc_macro2::{Spacing, TokenStream, TokenTree}; use proc_macro_utils::TokenStreamExt; use quote::{quote, ToTokens}; use syn::parse::{Parse, ParseStream}; use syn::{Result, Token}; use use_parser::{Use, UseItem}; mod prelude; mod use_parser; /// Internal, only used through macros in [`quote_use`](https://docs.rs/quote-use). /// Input is `quote_use_impl!(() ([span_expr =>]) /// ())`. #[proc_macro] pub fn quote_use_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let mut input = input.parser(); // This is internal, i.e., these expects are fine, they are not error handling. let path = input .next_group() .expect("there should be three `(...)`") .stream(); let span = input .next_group() .expect("there should be three `(...)`") .stream(); let uses = input .next_group() .expect("there should be three `(...)`") .stream(); let uses: QuoteUse = match syn::parse2(uses) { Ok(uses) => uses, Err(err) => return err.into_compile_error().into(), }; quote! { #path!{ #span #uses } } .into() } struct QuoteUse(Vec, TokenStream); impl Parse for QuoteUse { fn parse(input: ParseStream) -> Result { let mut uses = Vec::new(); while input.peek(Token![#]) && input.peek2(Token![use]) { input.parse::().expect("# was peeked before"); uses.extend_from_slice(&UseItem::parse(input)?.0); } Ok(QuoteUse(uses, input.parse()?)) } } impl ToTokens for QuoteUse { fn to_tokens(&self, tokens: &mut TokenStream) { let Self(uses, tail) = self; let mut prelude = true; let mut std = true; let mut uses: Vec<_> = uses .iter() .filter(|u| { if u.1 == "no_prelude" { prelude = false; false } else if u.1 == "no_std" { std = false; false } else { true } }) .cloned() .collect(); if prelude { uses.extend(prelude::prelude(std)); } tokens.extend(replace_in_group(&uses, tail.clone())); } } fn replace_in_group(uses: &[Use], tokens: TokenStream) -> TokenStream { use State::*; #[derive(Clone, Copy)] enum State { Path, Pound, Normal, } let mut state = Normal; tokens .into_iter() .flat_map(|token| { match (&token, state) { (TokenTree::Ident(ident), Normal) => { if let Some(Use(path, _)) = uses.iter().find(|item| &item.1 == ident) { return quote!(#path); } } // first colon (TokenTree::Punct(punct), _) if punct.spacing() == Spacing::Joint && punct.as_char() == ':' => { state = Path; } // second colon (TokenTree::Punct(punct), _) if punct.as_char() == ':' => (), // quote var `#ident` (TokenTree::Punct(punct), _) if punct.as_char() == '#' => { state = Pound; } (TokenTree::Group(group), _) => { let tokens = replace_in_group(uses, group.stream()); return match group.delimiter() { proc_macro2::Delimiter::Parenthesis => quote!((#tokens)), proc_macro2::Delimiter::Brace => quote!({#tokens}), proc_macro2::Delimiter::Bracket => quote!([#tokens]), proc_macro2::Delimiter::None => tokens, }; } _ => { state = Normal; } }; quote!(#token) }) .collect() } quote-use-macros-0.8.4/src/prelude/2021.rs000064400000000000000000000001001046102023000162160ustar 00000000000000use core::prelude::rust_2021::{FromIterator, TryFrom, TryInto}; quote-use-macros-0.8.4/src/prelude/core.rs000064400000000000000000000011351046102023000165730ustar 00000000000000use core::prelude::v1::{ assert, bench, cfg, cfg_accessible, cfg_eval, column, compile_error, concat, concat_bytes, concat_idents, derive, drop, env, file, format_args, format_args_nl, global_allocator, include, include_bytes, include_str, line, log_syntax, module_path, option_env, stringify, test, test_case, trace_macros, AsMut, AsRef, Clone, Copy, Debug, Default, DoubleEndedIterator, Drop, Eq, Err, ExactSizeIterator, Extend, Fn, FnMut, FnOnce, From, Hash, Into, IntoIterator, Iterator, None, Ok, Option, Ord, PartialEq, PartialOrd, Result, Send, Sized, Some, Sync, Unpin, }; quote-use-macros-0.8.4/src/prelude/std.rs000064400000000000000000000001031046102023000164270ustar 00000000000000use std::prelude::v1::{ ToOwned, Box, String, ToString, Vec }; quote-use-macros-0.8.4/src/prelude.rs000064400000000000000000000013661046102023000156510ustar 00000000000000use syn::parse::Parser; use syn::punctuated::Punctuated; use syn::Token; use crate::use_parser::UseItem; use crate::Use; pub(crate) fn prelude(std: bool) -> Box> { let prelude = parse_prelude(include_str!("prelude/core.rs")); if std { let prelude = prelude.chain(parse_prelude(include_str!("prelude/std.rs"))); let prelude = prelude.chain(parse_prelude(include_str!("prelude/2021.rs"))); Box::new(prelude) } else { Box::new(prelude) } } fn parse_prelude(file: &str) -> impl Iterator { Punctuated::::parse_terminated .parse_str(file) .expect("prelude should be valid") .into_iter() .flat_map(|u| u.0.into_iter()) } quote-use-macros-0.8.4/src/use_parser.rs000064400000000000000000000140551046102023000163600ustar 00000000000000use proc_macro2::{Ident, TokenStream, TokenTree}; use quote::{quote, ToTokens}; use syn::ext::IdentExt; use syn::parse::{Parse, ParseStream}; use syn::token::Brace; use syn::{braced, Error, Result, Token}; #[derive(Debug, Clone)] pub enum IdentOrPounded { Ident(Ident), Pounded(Token![#], TokenTree), } impl IdentOrPounded { fn is_self(&self) -> bool { if let Self::Ident(ident) = self { ident == "self" } else { false } } fn is_ident(&self) -> bool { matches!(self, Self::Ident(_)) } } impl ToTokens for IdentOrPounded { fn to_tokens(&self, tokens: &mut TokenStream) { match self { IdentOrPounded::Ident(ident) => ident.to_tokens(tokens), IdentOrPounded::Pounded(pound, tt) => { pound.to_tokens(tokens); tt.to_tokens(tokens); } } } } impl Parse for IdentOrPounded { fn parse(input: ParseStream) -> Result { Ident::parse_any(input) .map(Self::Ident) .or_else(|_| Ok(Self::Pounded(input.parse()?, input.parse()?))) } } #[derive(Clone, Debug, Default)] pub struct Path(Vec); impl Path { fn push(&mut self, value: IdentOrPounded) { self.0.push(value); } fn pop_self(&mut self) -> bool { self.0.last().map_or(false, IdentOrPounded::is_self) && { self.pop(); true } } fn get_ident(&self) -> Result<&Ident> { match self.0.last().expect("path should contain a segment") { IdentOrPounded::Ident(ident) => Ok(ident), IdentOrPounded::Pounded(pound, _) => Err(Error::new_spanned( pound, "expected ident as last path segment", )), } } fn pop(&mut self) { self.0 .pop() .expect("path should contain at least one segment"); } } impl ToTokens for Path { fn to_tokens(&self, tokens: &mut TokenStream) { let first = self.0.first().expect("path should contain a segment"); let colons = first.is_ident().then_some(quote!(::)); let tail = &self.0[1..]; quote!(#colons #first #(::#tail)*).to_tokens(tokens) } } #[derive(Clone, Debug)] pub struct Use(pub Path, pub Ident); #[derive(Clone, Debug, Default)] pub struct UseItem(pub Vec); // INPUTS: // a::b::{a::{}, b} fn parse_use_segment( parent: &Path, input: ParseStream, output: &mut Vec, inner: bool, ) -> Result<()> { let mut path = parent.clone(); loop { let la = input.lookahead1(); if la.peek(Ident::peek_any) || la.peek(Token![#]) { path.push(input.parse()?); let la = input.lookahead1(); if inner && (la.peek(Token![,]) || input.is_empty()) || !inner && la.peek(Token![;]) { path.pop_self(); output.push(Use(path.clone(), path.get_ident()?.clone())); break; } else if la.peek(Token![as]) { input.parse::()?; let alias: Ident = input.parse()?; path.pop_self(); output.push(Use(path, alias)); break; } else if la.peek(Token![::]) { input.parse::()?; continue; } else { return Err(la.error()); } } else if la.peek(Brace) { // A group let content; braced!(content in input); loop { parse_use_segment(&path, &content, output, true)?; if content.is_empty() { break; } else { content.parse::()?; if content.is_empty() { break; } } } let la = input.lookahead1(); if inner && (input.is_empty() || la.peek(Token![,])) || !inner && la.peek(Token![;]) { break; } else { return Err(la.error()); } } else { return Err(la.error()); } } Ok(()) } impl Parse for UseItem { fn parse(input: ParseStream) -> Result { if input.is_empty() { return Ok(Self::default()); } let mut output = Vec::new(); ::parse(input)?; Option::::parse(input)?; parse_use_segment(&Default::default(), input, &mut output, false)?; ::parse(input)?; Ok(Self(output)) } } #[cfg(test)] mod test { use pretty_assertions::assert_eq; use quote::ToTokens; use syn::parse::Parser; use syn::parse_str; use super::*; macro_rules! assert_use_item { ($use:literal, $($path:literal as $ident:ident),* $(,)*) => { let UseItem(uses) = parse_str($use).unwrap(); let mut uses = uses.into_iter(); $( let Use(path, ident) = uses.next().unwrap(); assert_eq!(path.into_token_stream().to_string().replace(' ', ""), $path); assert_eq!(ident, stringify!($ident)); )* }; } #[test] fn use_item() { assert_use_item!("use ::a::b;", "::a::b" as b); assert_use_item!( "use a::{c, self, b};", "::a::c" as c, "::a" as a, "::a::b" as b ); assert_use_item!("use a::{self as c, b as a};", "::a" as c, "::a::b" as a); assert_use_item!( "use a::{b::{a, b}, c};", "::a::b::a" as a, "::a::b::b" as b, "::a::c" as c ); assert_use_item!("use #var::a;", "#var::a" as a); assert_use_item!("use ::a::#var::a;", "::a::#var::a" as a); assert_use_item!("use ::a::#var as a;", "::a::#var" as a); } macro_rules! assert_error { ($use:literal) => { UseItem::parse.parse_str($use).unwrap_err(); }; } #[test] fn error() { assert_error!("use ::a::#b;"); } }