From d29a20e44207f915f42e6d1668d2864b0152e5a8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 17 Mar 2020 11:55:56 -0700 Subject: [PATCH 01/93] Release proc-macro-nested 0.1.4 --- nested/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nested/Cargo.toml b/nested/Cargo.toml index 9104685..3d64155 100644 --- a/nested/Cargo.toml +++ b/nested/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-nested" -version = "0.1.3" +version = "0.1.4" authors = ["David Tolnay "] license = "MIT OR Apache-2.0" description = "Support for nested proc-macro-hack invocations" From 39b7c70ac9b7c595881107df417c6423d7e2eb51 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 17 Mar 2020 13:29:27 -0700 Subject: [PATCH 02/93] Select a single docs.rs build target --- Cargo.toml | 3 +++ demo-hack-impl/Cargo.toml | 3 +++ demo-hack/Cargo.toml | 3 +++ nested/Cargo.toml | 3 +++ 4 files changed, 12 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index ddc7286..28ccdd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,6 @@ travis-ci = { repository = "dtolnay/proc-macro-hack" } [workspace] members = ["demo-hack", "demo-hack-impl", "example", "nested"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/demo-hack-impl/Cargo.toml b/demo-hack-impl/Cargo.toml index 2540d37..dc9d85e 100644 --- a/demo-hack-impl/Cargo.toml +++ b/demo-hack-impl/Cargo.toml @@ -14,3 +14,6 @@ proc-macro = true proc-macro-hack = { version = "0.5", path = ".." } quote = "1.0" syn = { version = "1.0", features = ["full"] } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/demo-hack/Cargo.toml b/demo-hack/Cargo.toml index d55e374..238b96c 100644 --- a/demo-hack/Cargo.toml +++ b/demo-hack/Cargo.toml @@ -10,3 +10,6 @@ edition = "2018" [dependencies] proc-macro-hack = { version = "0.5", path = ".." } demo-hack-impl = { version = "0.0.5", path = "../demo-hack-impl" } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/nested/Cargo.toml b/nested/Cargo.toml index 3d64155..a6d323f 100644 --- a/nested/Cargo.toml +++ b/nested/Cargo.toml @@ -5,3 +5,6 @@ authors = ["David Tolnay "] license = "MIT OR Apache-2.0" description = "Support for nested proc-macro-hack invocations" repository = "https://github.com/dtolnay/proc-macro-hack" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] From 5bd6e77960b19198c99587717fe1bb37aa6bad0c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 11:04:42 -0700 Subject: [PATCH 03/93] Move "unexpected input" error to the unexpected token --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index e9b80e6..6f7d826 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -173,7 +173,7 @@ impl Parse for Input { } else if ahead.peek(Token![fn]) { input.parse().map(Input::Define) } else { - Err(input.error("unexpected input to #[proc_macro_hack]")) + Err(ahead.error("unexpected input to #[proc_macro_hack]")) } } } From 00696fd00753f07b4f82c5bee793bea017f7c406 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 11:04:56 -0700 Subject: [PATCH 04/93] Add trybuild tests --- Cargo.toml | 2 ++ tests/compiletest.rs | 6 ++++++ tests/ui/private.rs | 8 ++++++++ tests/ui/private.stderr | 5 +++++ tests/ui/unexpected.rs | 6 ++++++ tests/ui/unexpected.stderr | 5 +++++ 6 files changed, 32 insertions(+) create mode 100644 tests/compiletest.rs create mode 100644 tests/ui/private.rs create mode 100644 tests/ui/private.stderr create mode 100644 tests/ui/unexpected.rs create mode 100644 tests/ui/unexpected.stderr diff --git a/Cargo.toml b/Cargo.toml index 28ccdd4..7ef0234 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,8 @@ syn = "1.0.5" [dev-dependencies] demo-hack = { version = "0.0.5", path = "demo-hack" } demo-hack-impl = { version = "0.0.5", path = "demo-hack-impl" } +rustversion = "1.0" +trybuild = "1.0" [badges] travis-ci = { repository = "dtolnay/proc-macro-hack" } diff --git a/tests/compiletest.rs b/tests/compiletest.rs new file mode 100644 index 0000000..f9aea23 --- /dev/null +++ b/tests/compiletest.rs @@ -0,0 +1,6 @@ +#[rustversion::attr(not(nightly), ignore)] +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +} diff --git a/tests/ui/private.rs b/tests/ui/private.rs new file mode 100644 index 0000000..1ab8444 --- /dev/null +++ b/tests/ui/private.rs @@ -0,0 +1,8 @@ +use proc_macro_hack::proc_macro_hack; + +#[proc_macro_hack] +fn my_macro(input: TokenStream) -> TokenStream { + unimplemented!() +} + +fn main() {} diff --git a/tests/ui/private.stderr b/tests/ui/private.stderr new file mode 100644 index 0000000..be79443 --- /dev/null +++ b/tests/ui/private.stderr @@ -0,0 +1,5 @@ +error: functions tagged with `#[proc_macro_hack]` must be `pub` + --> $DIR/private.rs:4:1 + | +4 | fn my_macro(input: TokenStream) -> TokenStream { + | ^^ diff --git a/tests/ui/unexpected.rs b/tests/ui/unexpected.rs new file mode 100644 index 0000000..122ded5 --- /dev/null +++ b/tests/ui/unexpected.rs @@ -0,0 +1,6 @@ +use proc_macro_hack::proc_macro_hack; + +#[proc_macro_hack] +pub struct What; + +fn main() {} diff --git a/tests/ui/unexpected.stderr b/tests/ui/unexpected.stderr new file mode 100644 index 0000000..e3da97a --- /dev/null +++ b/tests/ui/unexpected.stderr @@ -0,0 +1,5 @@ +error: unexpected input to #[proc_macro_hack] + --> $DIR/unexpected.rs:4:5 + | +4 | pub struct What; + | ^^^^^^ From 9d0857810b72ea46c5e438c0878fee48ecd67752 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 11:11:42 -0700 Subject: [PATCH 05/93] Exclude dev dependencies from minimal-versions check --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 320266a..1a5134a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,8 @@ matrix: name: Minimal versions before_script: - cargo update -Z minimal-versions --manifest-path example/Cargo.toml + script: + - cargo check --manifest-path example/Cargo.toml - rust: 1.31.0 name: 2015 edition script: From 6eb59e0160e3d1e8880cbb35f8efbe2c69291638 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 12:42:47 -0700 Subject: [PATCH 06/93] Add arg parsing ui test --- tests/ui/unknown-arg.rs | 6 ++++++ tests/ui/unknown-arg.stderr | 5 +++++ 2 files changed, 11 insertions(+) create mode 100644 tests/ui/unknown-arg.rs create mode 100644 tests/ui/unknown-arg.stderr diff --git a/tests/ui/unknown-arg.rs b/tests/ui/unknown-arg.rs new file mode 100644 index 0000000..aa9b82c --- /dev/null +++ b/tests/ui/unknown-arg.rs @@ -0,0 +1,6 @@ +use proc_macro_hack::proc_macro_hack; + +#[proc_macro_hack(fake_call_site, support_nexted)] +pub use demo::some_macro; + +fn main() {} diff --git a/tests/ui/unknown-arg.stderr b/tests/ui/unknown-arg.stderr new file mode 100644 index 0000000..d3cd8c9 --- /dev/null +++ b/tests/ui/unknown-arg.stderr @@ -0,0 +1,5 @@ +error: expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site` + --> $DIR/unknown-arg.rs:3:35 + | +3 | #[proc_macro_hack(fake_call_site, support_nexted)] + | ^^^^^^^^^^^^^^ From 1ff9e1358f25ca1fd2286effca5ae639691018d1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 11:00:19 -0700 Subject: [PATCH 07/93] Replace syn dependency with parsing TokenStream --- Cargo.toml | 2 +- src/lib.rs | 419 ++++++++++++++++++++++++++++++++--------------------- 2 files changed, 257 insertions(+), 164 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7ef0234..c7feb34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,12 +15,12 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -syn = "1.0.5" [dev-dependencies] demo-hack = { version = "0.0.5", path = "demo-hack" } demo-hack-impl = { version = "0.0.5", path = "demo-hack-impl" } rustversion = "1.0" +syn = "1.0.5" trybuild = "1.0" [badges] diff --git a/src/lib.rs b/src/lib.rs index 6f7d826..08019b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,14 +128,14 @@ extern crate proc_macro; -use proc_macro2::{Span, TokenStream, TokenTree}; -use quote::{format_ident, quote, ToTokens}; +use proc_macro2::Delimiter::{Brace, Bracket, Parenthesis}; +use proc_macro2::{token_stream, Delimiter, Ident, Span, TokenStream, TokenTree}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::fmt::Write; -use syn::ext::IdentExt; -use syn::parse::{Parse, ParseStream, Result}; -use syn::{braced, bracketed, parenthesized, parse_macro_input, token, Ident, LitInt, Token}; +use std::iter::Peekable; -type Visibility = Option; +type Iter<'a> = &'a mut Peekable; +type Visibility = Option; enum Input { Export(Export), @@ -162,96 +162,178 @@ struct Macro { export_as: Ident, } -impl Parse for Input { - fn parse(input: ParseStream) -> Result { - let ahead = input.fork(); - parse_attributes(&ahead)?; - ahead.parse::()?; +struct Error { + span: Span, + msg: String, +} - if ahead.peek(Token![use]) { - input.parse().map(Input::Export) - } else if ahead.peek(Token![fn]) { - input.parse().map(Input::Define) - } else { - Err(ahead.error("unexpected input to #[proc_macro_hack]")) +impl Error { + fn new(span: Span, msg: impl Into) -> Self { + Error { + span, + msg: msg.into(), } } } -impl Parse for Export { - fn parse(input: ParseStream) -> Result { - let attrs = input.call(parse_attributes)?; - let vis: Visibility = input.parse()?; - input.parse::()?; - input.parse::>()?; - let from: Ident = input.parse()?; - input.parse::()?; +fn parse_input(tokens: Iter) -> Result { + let attrs = parse_attributes(tokens)?; + let vis = parse_visibility(tokens)?; + let kw = parse_ident(tokens)?; + if kw == "use" { + parse_export(attrs, vis, tokens).map(Input::Export) + } else if kw == "fn" { + parse_define(attrs, vis, kw.span(), tokens).map(Input::Define) + } else { + Err(Error::new( + kw.span(), + "unexpected input to #[proc_macro_hack]", + )) + } +} - let mut macros = Vec::new(); - if input.peek(token::Brace) { - let content; - braced!(content in input); +fn parse_export(attrs: TokenStream, vis: Visibility, tokens: Iter) -> Result { + let _ = parse_punct(tokens, ':'); + let _ = parse_punct(tokens, ':'); + let from = parse_ident(tokens)?; + parse_punct(tokens, ':')?; + parse_punct(tokens, ':')?; + + let mut macros = Vec::new(); + match tokens.peek() { + Some(TokenTree::Group(group)) if group.delimiter() == Brace => { + let ref mut content = group.stream().into_iter().peekable(); loop { - macros.push(content.parse()?); - if content.is_empty() { + macros.push(parse_macro(content)?); + if content.peek().is_none() { break; } - content.parse::()?; - if content.is_empty() { + parse_punct(content, ',')?; + if content.peek().is_none() { break; } } - } else { - macros.push(input.parse()?); } + _ => macros.push(parse_macro(tokens)?), + } - input.parse::()?; - Ok(Export { - attrs, - vis, - from, - macros, - }) + parse_punct(tokens, ';')?; + Ok(Export { + attrs, + vis, + from, + macros, + }) +} + +fn parse_punct(tokens: Iter, ch: char) -> Result<(), Error> { + match tokens.peek() { + Some(TokenTree::Punct(punct)) if punct.as_char() == ch => { + tokens.next().unwrap(); + Ok(()) + } + tt => Err(Error::new( + tt.map_or_else(Span::call_site, TokenTree::span), + format!("expected `{}`", ch), + )), } } -impl Parse for Define { - fn parse(input: ParseStream) -> Result { - let attrs = input.call(parse_attributes)?; - let vis: Visibility = input.parse()?; - if vis.is_none() { - return Err(input.error("functions tagged with `#[proc_macro_hack]` must be `pub`")); +fn parse_define( + attrs: TokenStream, + vis: Visibility, + fn_token: Span, + tokens: Iter, +) -> Result { + if vis.is_none() { + return Err(Error::new( + fn_token, + "functions tagged with `#[proc_macro_hack]` must be `pub`", + )); + } + let name = parse_ident(tokens)?; + let body = tokens.collect(); + Ok(Define { attrs, name, body }) +} + +fn parse_macro(tokens: Iter) -> Result { + let name = parse_ident(tokens)?; + let export_as = match tokens.peek() { + Some(TokenTree::Ident(ident)) if ident == "as" => { + tokens.next().unwrap(); + parse_ident(tokens)? } + _ => name.clone(), + }; + Ok(Macro { name, export_as }) +} - input.parse::()?; - let name: Ident = input.parse()?; - let body: TokenStream = input.parse()?; - Ok(Define { attrs, name, body }) +fn parse_ident(tokens: Iter) -> Result { + match tokens.next() { + Some(TokenTree::Ident(ident)) => Ok(ident), + tt => Err(Error::new( + tt.as_ref().map_or_else(Span::call_site, TokenTree::span), + "expected identifier", + )), } } -impl Parse for Macro { - fn parse(input: ParseStream) -> Result { - let name: Ident = input.parse()?; - let renamed: Option = input.parse()?; - let export_as = if renamed.is_some() { - input.parse()? - } else { - name.clone() - }; - Ok(Macro { name, export_as }) +fn parse_keyword(tokens: Iter, kw: &'static str) -> Result<(), Error> { + match &tokens.next() { + Some(TokenTree::Ident(ident)) if ident == kw => Ok(()), + tt => Err(Error::new( + tt.as_ref().map_or_else(Span::call_site, TokenTree::span), + format!("expected `{}`", kw), + )), + } +} + +fn parse_int(tokens: Iter) -> Result { + match tokens.next() { + Some(TokenTree::Literal(lit)) => lit.to_string().parse().map_err(|_| lit.span()), + Some(tt) => Err(tt.span()), + None => Err(Span::call_site()), + } +} + +fn parse_group( + tokens: Iter, + delimiter: Delimiter, +) -> Result, Error> { + match &tokens.next() { + Some(TokenTree::Group(group)) if group.delimiter() == delimiter => { + Ok(group.stream().into_iter().peekable()) + } + tt => Err(Error::new( + tt.as_ref().map_or_else(Span::call_site, TokenTree::span), + "expected delimiter", + )), + } +} + +fn parse_visibility(tokens: Iter) -> Result { + if let Some(TokenTree::Ident(ident)) = tokens.peek() { + if ident == "pub" { + return Ok(Some(tokens.next().unwrap().span())); + } } + Ok(None) } -fn parse_attributes(input: ParseStream) -> Result { +fn parse_attributes(tokens: Iter) -> Result { let mut attrs = TokenStream::new(); - while input.peek(Token![#]) { - let pound: Token![#] = input.parse()?; - pound.to_tokens(&mut attrs); - let content; - let bracket_token = bracketed!(content in input); - let content: TokenStream = content.parse()?; - bracket_token.surround(&mut attrs, |tokens| content.to_tokens(tokens)); + while let Some(TokenTree::Punct(punct)) = tokens.peek() { + if punct.as_char() != '#' { + break; + } + let span = punct.span(); + attrs.extend(tokens.next()); + match tokens.peek() { + Some(TokenTree::Group(group)) if group.delimiter() == Bracket => { + attrs.extend(tokens.next()); + } + _ => return Err(Error::new(span, "unexpected input")), + } } Ok(attrs) } @@ -261,23 +343,29 @@ pub fn proc_macro_hack( args: proc_macro::TokenStream, input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - proc_macro::TokenStream::from(match parse_macro_input!(input) { + let ref mut args = TokenStream::from(args).into_iter().peekable(); + let ref mut input = TokenStream::from(input).into_iter().peekable(); + let output = expand_proc_macro_hack(args, input).unwrap_or_else(compile_error); + proc_macro::TokenStream::from(output) +} + +fn expand_proc_macro_hack(args: Iter, input: Iter) -> Result { + match parse_input(input)? { Input::Export(export) => { - let args = parse_macro_input!(args as ExportArgs); - expand_export(export, args) + let args = parse_export_args(args)?; + Ok(expand_export(export, args)) } Input::Define(define) => { - parse_macro_input!(args as DefineArgs); - expand_define(define) + parse_define_args(args)?; + Ok(expand_define(define)) } - }) + } } -mod kw { - syn::custom_keyword!(derive); - syn::custom_keyword!(fake_call_site); - syn::custom_keyword!(internal_macro_calls); - syn::custom_keyword!(support_nested); +fn compile_error(err: Error) -> TokenStream { + let span = err.span; + let msg = err.msg; + quote_spanned!(span=> compile_error! { #msg }) } struct ExportArgs { @@ -286,86 +374,82 @@ struct ExportArgs { fake_call_site: bool, } -impl Parse for ExportArgs { - fn parse(input: ParseStream) -> Result { - let mut args = ExportArgs { - support_nested: false, - internal_macro_calls: 0, - fake_call_site: false, - }; +fn parse_export_args(tokens: Iter) -> Result { + let mut args = ExportArgs { + support_nested: false, + internal_macro_calls: 0, + fake_call_site: false, + }; - while !input.is_empty() { - let ahead = input.lookahead1(); - if ahead.peek(kw::support_nested) { - input.parse::()?; + while let Some(tt) = tokens.next() { + match &tt { + TokenTree::Ident(ident) if ident == "support_nested" => { args.support_nested = true; - } else if ahead.peek(kw::internal_macro_calls) { - input.parse::()?; - input.parse::()?; - let calls = input.parse::()?.base10_parse()?; + } + TokenTree::Ident(ident) if ident == "internal_macro_calls" => { + parse_punct(tokens, '=')?; + let calls = parse_int(tokens).map_err(|span| { + Error::new(span, "expected integer value for internal_macro_calls") + })?; args.internal_macro_calls = calls; - } else if ahead.peek(kw::fake_call_site) { - input.parse::()?; + } + TokenTree::Ident(ident) if ident == "fake_call_site" => { args.fake_call_site = true; - } else { - return Err(ahead.error()); } - if input.is_empty() { - break; + _ => { + return Err(Error::new( + tt.span(), + "expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`", + )) } - input.parse::()?; } - - Ok(args) + if tokens.peek().is_none() { + break; + } + parse_punct(tokens, ',')?; } -} -struct DefineArgs; - -impl Parse for DefineArgs { - fn parse(_input: ParseStream) -> Result { - Ok(DefineArgs) - } + Ok(args) } -struct EnumHack { - token_stream: TokenStream, +fn parse_define_args(tokens: Iter) -> Result<(), Error> { + if tokens.peek().is_none() { + Ok(()) + } else { + Err(Error::new(Span::call_site(), "unexpected input")) + } } -impl Parse for EnumHack { - fn parse(input: ParseStream) -> Result { - input.parse::()?; - input.parse::()?; +fn parse_enum_hack(tokens: Iter) -> Result { + parse_keyword(tokens, "enum")?; + parse_ident(tokens)?; - let braces; - braced!(braces in input); - braces.parse::()?; - braces.parse::()?; + let ref mut braces = parse_group(tokens, Brace)?; + parse_ident(braces)?; + parse_punct(braces, '=')?; - let parens; - parenthesized!(parens in braces); - parens.parse::()?; - parens.parse::()?; + let ref mut parens = parse_group(braces, Parenthesis)?; + parse_ident(parens)?; + parse_punct(parens, '!')?; - let inner; - braced!(inner in parens); - let token_stream: TokenStream = inner.parse()?; + let ref mut inner = parse_group(parens, Brace)?; + let token_stream = inner.collect(); - parens.parse::()?; - parens.parse::()?; - braces.parse::()?; - braces.parse::()?; - braces.parse::()?; + parse_punct(parens, ',')?; + let _ = parens.next(); + parse_punct(braces, '.')?; + let _ = braces.next(); + parse_punct(braces, ',')?; - Ok(EnumHack { token_stream }) - } + Ok(token_stream) } #[doc(hidden)] #[proc_macro_derive(ProcMacroHack)] pub fn enum_hack(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let inner = parse_macro_input!(input as EnumHack); - proc_macro::TokenStream::from(inner.token_stream) + let ref mut input = TokenStream::from(input).into_iter().peekable(); + let output = parse_enum_hack(input).unwrap_or_else(compile_error); + proc_macro::TokenStream::from(output) } struct FakeCallSite { @@ -373,19 +457,15 @@ struct FakeCallSite { rest: TokenStream, } -impl Parse for FakeCallSite { - fn parse(input: ParseStream) -> Result { - input.parse::()?; - let attr; - bracketed!(attr in input); - attr.parse::()?; - let path; - parenthesized!(path in attr); - Ok(FakeCallSite { - derive: path.parse()?, - rest: input.parse()?, - }) - } +fn parse_fake_call_site(tokens: Iter) -> Result { + parse_punct(tokens, '#')?; + let ref mut attr = parse_group(tokens, Bracket)?; + parse_keyword(attr, "derive")?; + let ref mut path = parse_group(attr, Parenthesis)?; + Ok(FakeCallSite { + derive: parse_ident(path)?, + rest: tokens.collect(), + }) } #[doc(hidden)] @@ -394,35 +474,39 @@ pub fn fake_call_site( args: proc_macro::TokenStream, input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - let args = TokenStream::from(args); - let span = match args.into_iter().next() { + let ref mut args = TokenStream::from(args).into_iter().peekable(); + let ref mut input = TokenStream::from(input).into_iter().peekable(); + let output = expand_fake_call_site(args, input).unwrap_or_else(compile_error); + proc_macro::TokenStream::from(output) +} + +fn expand_fake_call_site(args: Iter, input: Iter) -> Result { + let span = match args.next() { Some(token) => token.span(), - None => return input, + None => return Ok(input.collect()), }; - let input = parse_macro_input!(input as FakeCallSite); + let input = parse_fake_call_site(input)?; let mut derive = input.derive; derive.set_span(span); let rest = input.rest; - let expanded = quote! { + Ok(quote! { #[derive(#derive)] #rest - }; - - proc_macro::TokenStream::from(expanded) + }) } fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let dummy = dummy_name_for_export(&export); let attrs = export.attrs; - let vis = export.vis; + let ref vis = export.vis.map(|span| Ident::new("pub", span)); let macro_export = match vis { Some(_) => quote!(#[macro_export]), None => quote!(), }; - let crate_prefix = vis.map(|_| quote!($crate::)); + let crate_prefix = vis.as_ref().map(|_| quote!($crate::)); let enum_variant = if args.support_nested { if args.internal_macro_calls == 0 { quote!(Nested) @@ -637,15 +721,24 @@ fn call_site_macro_name(conceptual: &Ident) -> Ident { fn dummy_name_for_export(export: &Export) -> String { let mut dummy = String::new(); - let from = export.from.unraw().to_string(); + let from = unraw(&export.from).to_string(); write!(dummy, "_{}{}", from.len(), from).unwrap(); for m in &export.macros { - let name = m.name.unraw().to_string(); + let name = unraw(&m.name).to_string(); write!(dummy, "_{}{}", name.len(), name).unwrap(); } dummy } +fn unraw(ident: &Ident) -> Ident { + let string = ident.to_string(); + if string.starts_with("r#") { + Ident::new(&string[2..], ident.span()) + } else { + ident.clone() + } +} + fn wrap_in_enum_hack(dummy: String, inner: TokenStream) -> TokenStream { let dummy = Ident::new(&dummy, Span::call_site()); quote! { From afab4617ae02a723b3065b281be314621deb9a3c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 14:47:32 -0700 Subject: [PATCH 08/93] Move parsing to a parse module --- src/error.rs | 22 ++++ src/lib.rs | 285 +++------------------------------------------------ src/parse.rs | 247 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 282 insertions(+), 272 deletions(-) create mode 100644 src/error.rs create mode 100644 src/parse.rs diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..eafce14 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,22 @@ +use proc_macro2::{Span, TokenStream}; +use quote::quote_spanned; + +pub struct Error { + span: Span, + msg: String, +} + +impl Error { + pub fn new(span: Span, msg: impl Into) -> Self { + Error { + span, + msg: msg.into(), + } + } +} + +pub fn compile_error(err: Error) -> TokenStream { + let span = err.span; + let msg = err.msg; + quote_spanned!(span=> compile_error! { #msg }) +} diff --git a/src/lib.rs b/src/lib.rs index 08019b7..95837d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,9 +128,13 @@ extern crate proc_macro; -use proc_macro2::Delimiter::{Brace, Bracket, Parenthesis}; -use proc_macro2::{token_stream, Delimiter, Ident, Span, TokenStream, TokenTree}; -use quote::{format_ident, quote, quote_spanned, ToTokens}; +mod error; +mod parse; + +use crate::error::{compile_error, Error}; +use crate::parse::*; +use proc_macro2::{token_stream, Ident, Span, TokenStream}; +use quote::{format_ident, quote, ToTokens}; use std::fmt::Write; use std::iter::Peekable; @@ -162,182 +166,6 @@ struct Macro { export_as: Ident, } -struct Error { - span: Span, - msg: String, -} - -impl Error { - fn new(span: Span, msg: impl Into) -> Self { - Error { - span, - msg: msg.into(), - } - } -} - -fn parse_input(tokens: Iter) -> Result { - let attrs = parse_attributes(tokens)?; - let vis = parse_visibility(tokens)?; - let kw = parse_ident(tokens)?; - if kw == "use" { - parse_export(attrs, vis, tokens).map(Input::Export) - } else if kw == "fn" { - parse_define(attrs, vis, kw.span(), tokens).map(Input::Define) - } else { - Err(Error::new( - kw.span(), - "unexpected input to #[proc_macro_hack]", - )) - } -} - -fn parse_export(attrs: TokenStream, vis: Visibility, tokens: Iter) -> Result { - let _ = parse_punct(tokens, ':'); - let _ = parse_punct(tokens, ':'); - let from = parse_ident(tokens)?; - parse_punct(tokens, ':')?; - parse_punct(tokens, ':')?; - - let mut macros = Vec::new(); - match tokens.peek() { - Some(TokenTree::Group(group)) if group.delimiter() == Brace => { - let ref mut content = group.stream().into_iter().peekable(); - loop { - macros.push(parse_macro(content)?); - if content.peek().is_none() { - break; - } - parse_punct(content, ',')?; - if content.peek().is_none() { - break; - } - } - } - _ => macros.push(parse_macro(tokens)?), - } - - parse_punct(tokens, ';')?; - Ok(Export { - attrs, - vis, - from, - macros, - }) -} - -fn parse_punct(tokens: Iter, ch: char) -> Result<(), Error> { - match tokens.peek() { - Some(TokenTree::Punct(punct)) if punct.as_char() == ch => { - tokens.next().unwrap(); - Ok(()) - } - tt => Err(Error::new( - tt.map_or_else(Span::call_site, TokenTree::span), - format!("expected `{}`", ch), - )), - } -} - -fn parse_define( - attrs: TokenStream, - vis: Visibility, - fn_token: Span, - tokens: Iter, -) -> Result { - if vis.is_none() { - return Err(Error::new( - fn_token, - "functions tagged with `#[proc_macro_hack]` must be `pub`", - )); - } - let name = parse_ident(tokens)?; - let body = tokens.collect(); - Ok(Define { attrs, name, body }) -} - -fn parse_macro(tokens: Iter) -> Result { - let name = parse_ident(tokens)?; - let export_as = match tokens.peek() { - Some(TokenTree::Ident(ident)) if ident == "as" => { - tokens.next().unwrap(); - parse_ident(tokens)? - } - _ => name.clone(), - }; - Ok(Macro { name, export_as }) -} - -fn parse_ident(tokens: Iter) -> Result { - match tokens.next() { - Some(TokenTree::Ident(ident)) => Ok(ident), - tt => Err(Error::new( - tt.as_ref().map_or_else(Span::call_site, TokenTree::span), - "expected identifier", - )), - } -} - -fn parse_keyword(tokens: Iter, kw: &'static str) -> Result<(), Error> { - match &tokens.next() { - Some(TokenTree::Ident(ident)) if ident == kw => Ok(()), - tt => Err(Error::new( - tt.as_ref().map_or_else(Span::call_site, TokenTree::span), - format!("expected `{}`", kw), - )), - } -} - -fn parse_int(tokens: Iter) -> Result { - match tokens.next() { - Some(TokenTree::Literal(lit)) => lit.to_string().parse().map_err(|_| lit.span()), - Some(tt) => Err(tt.span()), - None => Err(Span::call_site()), - } -} - -fn parse_group( - tokens: Iter, - delimiter: Delimiter, -) -> Result, Error> { - match &tokens.next() { - Some(TokenTree::Group(group)) if group.delimiter() == delimiter => { - Ok(group.stream().into_iter().peekable()) - } - tt => Err(Error::new( - tt.as_ref().map_or_else(Span::call_site, TokenTree::span), - "expected delimiter", - )), - } -} - -fn parse_visibility(tokens: Iter) -> Result { - if let Some(TokenTree::Ident(ident)) = tokens.peek() { - if ident == "pub" { - return Ok(Some(tokens.next().unwrap().span())); - } - } - Ok(None) -} - -fn parse_attributes(tokens: Iter) -> Result { - let mut attrs = TokenStream::new(); - while let Some(TokenTree::Punct(punct)) = tokens.peek() { - if punct.as_char() != '#' { - break; - } - let span = punct.span(); - attrs.extend(tokens.next()); - match tokens.peek() { - Some(TokenTree::Group(group)) if group.delimiter() == Bracket => { - attrs.extend(tokens.next()); - } - _ => return Err(Error::new(span, "unexpected input")), - } - } - Ok(attrs) -} - #[proc_macro_attribute] pub fn proc_macro_hack( args: proc_macro::TokenStream, @@ -362,88 +190,6 @@ fn expand_proc_macro_hack(args: Iter, input: Iter) -> Result } } -fn compile_error(err: Error) -> TokenStream { - let span = err.span; - let msg = err.msg; - quote_spanned!(span=> compile_error! { #msg }) -} - -struct ExportArgs { - support_nested: bool, - internal_macro_calls: u16, - fake_call_site: bool, -} - -fn parse_export_args(tokens: Iter) -> Result { - let mut args = ExportArgs { - support_nested: false, - internal_macro_calls: 0, - fake_call_site: false, - }; - - while let Some(tt) = tokens.next() { - match &tt { - TokenTree::Ident(ident) if ident == "support_nested" => { - args.support_nested = true; - } - TokenTree::Ident(ident) if ident == "internal_macro_calls" => { - parse_punct(tokens, '=')?; - let calls = parse_int(tokens).map_err(|span| { - Error::new(span, "expected integer value for internal_macro_calls") - })?; - args.internal_macro_calls = calls; - } - TokenTree::Ident(ident) if ident == "fake_call_site" => { - args.fake_call_site = true; - } - _ => { - return Err(Error::new( - tt.span(), - "expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`", - )) - } - } - if tokens.peek().is_none() { - break; - } - parse_punct(tokens, ',')?; - } - - Ok(args) -} - -fn parse_define_args(tokens: Iter) -> Result<(), Error> { - if tokens.peek().is_none() { - Ok(()) - } else { - Err(Error::new(Span::call_site(), "unexpected input")) - } -} - -fn parse_enum_hack(tokens: Iter) -> Result { - parse_keyword(tokens, "enum")?; - parse_ident(tokens)?; - - let ref mut braces = parse_group(tokens, Brace)?; - parse_ident(braces)?; - parse_punct(braces, '=')?; - - let ref mut parens = parse_group(braces, Parenthesis)?; - parse_ident(parens)?; - parse_punct(parens, '!')?; - - let ref mut inner = parse_group(parens, Brace)?; - let token_stream = inner.collect(); - - parse_punct(parens, ',')?; - let _ = parens.next(); - parse_punct(braces, '.')?; - let _ = braces.next(); - parse_punct(braces, ',')?; - - Ok(token_stream) -} - #[doc(hidden)] #[proc_macro_derive(ProcMacroHack)] pub fn enum_hack(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -457,17 +203,6 @@ struct FakeCallSite { rest: TokenStream, } -fn parse_fake_call_site(tokens: Iter) -> Result { - parse_punct(tokens, '#')?; - let ref mut attr = parse_group(tokens, Bracket)?; - parse_keyword(attr, "derive")?; - let ref mut path = parse_group(attr, Parenthesis)?; - Ok(FakeCallSite { - derive: parse_ident(path)?, - rest: tokens.collect(), - }) -} - #[doc(hidden)] #[proc_macro_attribute] pub fn fake_call_site( @@ -497,6 +232,12 @@ fn expand_fake_call_site(args: Iter, input: Iter) -> Result }) } +struct ExportArgs { + support_nested: bool, + internal_macro_calls: u16, + fake_call_site: bool, +} + fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let dummy = dummy_name_for_export(&export); diff --git a/src/parse.rs b/src/parse.rs new file mode 100644 index 0000000..322fe49 --- /dev/null +++ b/src/parse.rs @@ -0,0 +1,247 @@ +use crate::{Define, Error, Export, ExportArgs, FakeCallSite, Input, Iter, Macro, Visibility}; +use proc_macro2::Delimiter::{Brace, Bracket, Parenthesis}; +use proc_macro2::{token_stream, Delimiter, Ident, Span, TokenStream, TokenTree}; +use std::iter::Peekable; + +pub(crate) fn parse_input(tokens: Iter) -> Result { + let attrs = parse_attributes(tokens)?; + let vis = parse_visibility(tokens)?; + let kw = parse_ident(tokens)?; + if kw == "use" { + parse_export(attrs, vis, tokens).map(Input::Export) + } else if kw == "fn" { + parse_define(attrs, vis, kw.span(), tokens).map(Input::Define) + } else { + Err(Error::new( + kw.span(), + "unexpected input to #[proc_macro_hack]", + )) + } +} + +fn parse_export(attrs: TokenStream, vis: Visibility, tokens: Iter) -> Result { + let _ = parse_punct(tokens, ':'); + let _ = parse_punct(tokens, ':'); + let from = parse_ident(tokens)?; + parse_punct(tokens, ':')?; + parse_punct(tokens, ':')?; + + let mut macros = Vec::new(); + match tokens.peek() { + Some(TokenTree::Group(group)) if group.delimiter() == Brace => { + let ref mut content = group.stream().into_iter().peekable(); + loop { + macros.push(parse_macro(content)?); + if content.peek().is_none() { + break; + } + parse_punct(content, ',')?; + if content.peek().is_none() { + break; + } + } + } + _ => macros.push(parse_macro(tokens)?), + } + + parse_punct(tokens, ';')?; + Ok(Export { + attrs, + vis, + from, + macros, + }) +} + +fn parse_punct(tokens: Iter, ch: char) -> Result<(), Error> { + match tokens.peek() { + Some(TokenTree::Punct(punct)) if punct.as_char() == ch => { + tokens.next().unwrap(); + Ok(()) + } + tt => Err(Error::new( + tt.map_or_else(Span::call_site, TokenTree::span), + format!("expected `{}`", ch), + )), + } +} + +fn parse_define( + attrs: TokenStream, + vis: Visibility, + fn_token: Span, + tokens: Iter, +) -> Result { + if vis.is_none() { + return Err(Error::new( + fn_token, + "functions tagged with `#[proc_macro_hack]` must be `pub`", + )); + } + let name = parse_ident(tokens)?; + let body = tokens.collect(); + Ok(Define { attrs, name, body }) +} + +fn parse_macro(tokens: Iter) -> Result { + let name = parse_ident(tokens)?; + let export_as = match tokens.peek() { + Some(TokenTree::Ident(ident)) if ident == "as" => { + tokens.next().unwrap(); + parse_ident(tokens)? + } + _ => name.clone(), + }; + Ok(Macro { name, export_as }) +} + +fn parse_ident(tokens: Iter) -> Result { + match tokens.next() { + Some(TokenTree::Ident(ident)) => Ok(ident), + tt => Err(Error::new( + tt.as_ref().map_or_else(Span::call_site, TokenTree::span), + "expected identifier", + )), + } +} + +fn parse_keyword(tokens: Iter, kw: &'static str) -> Result<(), Error> { + match &tokens.next() { + Some(TokenTree::Ident(ident)) if ident == kw => Ok(()), + tt => Err(Error::new( + tt.as_ref().map_or_else(Span::call_site, TokenTree::span), + format!("expected `{}`", kw), + )), + } +} + +fn parse_int(tokens: Iter) -> Result { + match tokens.next() { + Some(TokenTree::Literal(lit)) => lit.to_string().parse().map_err(|_| lit.span()), + Some(tt) => Err(tt.span()), + None => Err(Span::call_site()), + } +} + +fn parse_group( + tokens: Iter, + delimiter: Delimiter, +) -> Result, Error> { + match &tokens.next() { + Some(TokenTree::Group(group)) if group.delimiter() == delimiter => { + Ok(group.stream().into_iter().peekable()) + } + tt => Err(Error::new( + tt.as_ref().map_or_else(Span::call_site, TokenTree::span), + "expected delimiter", + )), + } +} + +fn parse_visibility(tokens: Iter) -> Result { + if let Some(TokenTree::Ident(ident)) = tokens.peek() { + if ident == "pub" { + return Ok(Some(tokens.next().unwrap().span())); + } + } + Ok(None) +} + +fn parse_attributes(tokens: Iter) -> Result { + let mut attrs = TokenStream::new(); + while let Some(TokenTree::Punct(punct)) = tokens.peek() { + if punct.as_char() != '#' { + break; + } + let span = punct.span(); + attrs.extend(tokens.next()); + match tokens.peek() { + Some(TokenTree::Group(group)) if group.delimiter() == Bracket => { + attrs.extend(tokens.next()); + } + _ => return Err(Error::new(span, "unexpected input")), + } + } + Ok(attrs) +} + +pub(crate) fn parse_export_args(tokens: Iter) -> Result { + let mut args = ExportArgs { + support_nested: false, + internal_macro_calls: 0, + fake_call_site: false, + }; + + while let Some(tt) = tokens.next() { + match &tt { + TokenTree::Ident(ident) if ident == "support_nested" => { + args.support_nested = true; + } + TokenTree::Ident(ident) if ident == "internal_macro_calls" => { + parse_punct(tokens, '=')?; + let calls = parse_int(tokens).map_err(|span| { + Error::new(span, "expected integer value for internal_macro_calls") + })?; + args.internal_macro_calls = calls; + } + TokenTree::Ident(ident) if ident == "fake_call_site" => { + args.fake_call_site = true; + } + _ => { + return Err(Error::new( + tt.span(), + "expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`", + )) + } + } + if tokens.peek().is_none() { + break; + } + parse_punct(tokens, ',')?; + } + + Ok(args) +} + +pub(crate) fn parse_define_args(tokens: Iter) -> Result<(), Error> { + if tokens.peek().is_none() { + Ok(()) + } else { + Err(Error::new(Span::call_site(), "unexpected input")) + } +} + +pub(crate) fn parse_enum_hack(tokens: Iter) -> Result { + parse_keyword(tokens, "enum")?; + parse_ident(tokens)?; + + let ref mut braces = parse_group(tokens, Brace)?; + parse_ident(braces)?; + parse_punct(braces, '=')?; + + let ref mut parens = parse_group(braces, Parenthesis)?; + parse_ident(parens)?; + parse_punct(parens, '!')?; + + let ref mut inner = parse_group(parens, Brace)?; + let token_stream = inner.collect(); + + parse_punct(parens, ',')?; + let _ = parens.next(); + parse_punct(braces, '.')?; + let _ = braces.next(); + parse_punct(braces, ',')?; + + Ok(token_stream) +} + +pub(crate) fn parse_fake_call_site(tokens: Iter) -> Result { + parse_punct(tokens, '#')?; + let ref mut attr = parse_group(tokens, Bracket)?; + parse_keyword(attr, "derive")?; + let ref mut path = parse_group(attr, Parenthesis)?; + Ok(FakeCallSite { + derive: parse_ident(path)?, + rest: tokens.collect(), + }) +} From 1a1913892bbd9a890c3ce15d6da2cd845b2daa35 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 14:40:41 -0700 Subject: [PATCH 09/93] Replace quote dependency --- Cargo.toml | 2 +- src/error.rs | 27 +++++++++--- src/lib.rs | 34 ++++++++++----- src/quote.rs | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 16 deletions(-) create mode 100644 src/quote.rs diff --git a/Cargo.toml b/Cargo.toml index c7feb34..d1fab6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,11 +14,11 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" -quote = "1.0" [dev-dependencies] demo-hack = { version = "0.0.5", path = "demo-hack" } demo-hack-impl = { version = "0.0.5", path = "demo-hack-impl" } +quote = "1.0" rustversion = "1.0" syn = "1.0.5" trybuild = "1.0" diff --git a/src/error.rs b/src/error.rs index eafce14..aef3eff 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ -use proc_macro2::{Span, TokenStream}; -use quote::quote_spanned; +use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; +use std::iter::FromIterator; pub struct Error { span: Span, @@ -16,7 +16,24 @@ impl Error { } pub fn compile_error(err: Error) -> TokenStream { - let span = err.span; - let msg = err.msg; - quote_spanned!(span=> compile_error! { #msg }) + // compile_error!($msg) + TokenStream::from_iter(vec![ + TokenTree::Ident(Ident::new("compile_error", err.span)), + TokenTree::Punct({ + let mut punct = Punct::new('!', Spacing::Alone); + punct.set_span(err.span); + punct + }), + TokenTree::Group({ + let mut group = Group::new(Delimiter::Brace, { + TokenStream::from_iter(vec![TokenTree::Literal({ + let mut string = Literal::string(&err.msg); + string.set_span(err.span); + string + })]) + }); + group.set_span(err.span); + group + }), + ]) } diff --git a/src/lib.rs b/src/lib.rs index 95837d9..8edddc8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,13 +128,15 @@ extern crate proc_macro; +#[macro_use] +mod quote; + mod error; mod parse; use crate::error::{compile_error, Error}; use crate::parse::*; -use proc_macro2::{token_stream, Ident, Span, TokenStream}; -use quote::{format_ident, quote, ToTokens}; +use proc_macro2::{token_stream, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; use std::fmt::Write; use std::iter::Peekable; @@ -250,12 +252,13 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let crate_prefix = vis.as_ref().map(|_| quote!($crate::)); let enum_variant = if args.support_nested { if args.internal_macro_calls == 0 { - quote!(Nested) + Ident::new("Nested", Span::call_site()) } else { - format_ident!("Nested{}", args.internal_macro_calls).to_token_stream() + let name = format!("Nested{}", args.internal_macro_calls); + Ident::new(&name, Span::call_site()) } } else { - quote!(Value) + Ident::new("Value", Span::call_site()) }; let from = export.from; @@ -277,9 +280,11 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { }; let proc_macro_call = if args.support_nested { - let extra_bangs = (0..args.internal_macro_calls).map(|_| quote!(!)); + let extra_bangs = (0..args.internal_macro_calls) + .map(|_| TokenTree::Punct(Punct::new('!', Spacing::Alone))) + .collect::(); quote! { - #crate_prefix #dispatch! { ($($proc_macro)*) #(#extra_bangs)* } + #crate_prefix #dispatch! { ($($proc_macro)*) #extra_bangs } } } else { quote! { @@ -449,15 +454,24 @@ fn expand_define(define: Define) -> TokenStream { } fn actual_proc_macro_name(conceptual: &Ident) -> Ident { - format_ident!("proc_macro_hack_{}", conceptual) + Ident::new( + &format!("proc_macro_hack_{}", conceptual), + conceptual.span(), + ) } fn dispatch_macro_name(conceptual: &Ident) -> Ident { - format_ident!("proc_macro_call_{}", conceptual) + Ident::new( + &format!("proc_macro_call_{}", conceptual), + conceptual.span(), + ) } fn call_site_macro_name(conceptual: &Ident) -> Ident { - format_ident!("proc_macro_fake_call_site_{}", conceptual) + Ident::new( + &format!("proc_macro_fake_call_site_{}", conceptual), + conceptual.span(), + ) } fn dummy_name_for_export(export: &Export) -> String { diff --git a/src/quote.rs b/src/quote.rs new file mode 100644 index 0000000..db2ddbe --- /dev/null +++ b/src/quote.rs @@ -0,0 +1,114 @@ +use proc_macro2::{Ident, TokenStream, TokenTree}; +use std::iter; + +macro_rules! quote { + () => { + ::proc_macro2::TokenStream::new() + }; + ($($tt:tt)*) => {{ + let mut tokens = ::proc_macro2::TokenStream::new(); + quote_each_token!(tokens $($tt)*); + tokens + }}; +} + +macro_rules! quote_each_token { + ($tokens:ident # $var:ident $($rest:tt)*) => { + $crate::quote::Tokens::extend(&mut $tokens, &$var); + quote_each_token!($tokens $($rest)*); + }; + ($tokens:ident $ident:ident $($rest:tt)*) => { + <::proc_macro2::TokenStream as ::std::iter::Extend<_>>::extend( + &mut $tokens, + ::std::iter::once( + ::proc_macro2::TokenTree::Ident( + ::proc_macro2::Ident::new( + stringify!($ident), + ::proc_macro2::Span::call_site(), + ), + ), + ), + ); + quote_each_token!($tokens $($rest)*); + }; + ($tokens:ident ( $($inner:tt)* ) $($rest:tt)*) => { + <::proc_macro2::TokenStream as ::std::iter::Extend<_>>::extend( + &mut $tokens, + ::std::iter::once( + ::proc_macro2::TokenTree::Group( + ::proc_macro2::Group::new( + ::proc_macro2::Delimiter::Parenthesis, + quote!($($inner)*), + ), + ), + ), + ); + quote_each_token!($tokens $($rest)*); + }; + ($tokens:ident [ $($inner:tt)* ] $($rest:tt)*) => { + <::proc_macro2::TokenStream as ::std::iter::Extend<_>>::extend( + &mut $tokens, + ::std::iter::once( + ::proc_macro2::TokenTree::Group( + ::proc_macro2::Group::new( + ::proc_macro2::Delimiter::Bracket, + quote!($($inner)*), + ), + ), + ), + ); + quote_each_token!($tokens $($rest)*); + }; + ($tokens:ident { $($inner:tt)* } $($rest:tt)*) => { + <::proc_macro2::TokenStream as ::std::iter::Extend<_>>::extend( + &mut $tokens, + ::std::iter::once( + ::proc_macro2::TokenTree::Group( + ::proc_macro2::Group::new( + ::proc_macro2::Delimiter::Brace, + quote!($($inner)*), + ), + ), + ), + ); + quote_each_token!($tokens $($rest)*); + }; + ($tokens:ident $punct:tt $($rest:tt)*) => { + <::proc_macro2::TokenStream as ::std::iter::Extend<_>>::extend( + &mut $tokens, + stringify!($punct).parse::<::proc_macro2::TokenStream>(), + ); + quote_each_token!($tokens $($rest)*); + }; + ($tokens:ident) => {}; +} + +pub trait Tokens { + fn extend(tokens: &mut TokenStream, var: &Self); +} + +impl Tokens for Ident { + fn extend(tokens: &mut TokenStream, var: &Self) { + tokens.extend(iter::once(TokenTree::Ident(var.clone()))); + } +} + +impl Tokens for TokenStream { + fn extend(tokens: &mut TokenStream, var: &Self) { + tokens.extend(var.clone()); + } +} + +impl Tokens for Option { + fn extend(tokens: &mut TokenStream, var: &Self) { + if let Some(var) = var { + T::extend(tokens, var); + } + } +} + +impl Tokens for &T { + fn extend(tokens: &mut TokenStream, var: &Self) { + T::extend(tokens, var); + } +} From 4614fb3396dcf88de11f90da2c659f9dca4657d5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 15:31:09 -0700 Subject: [PATCH 10/93] Replace proc-macro2 dependency --- Cargo.toml | 3 --- src/error.rs | 2 +- src/lib.rs | 33 ++++++++++++--------------------- src/parse.rs | 20 ++++++++++---------- src/quote.rs | 42 +++++++++++++++++++++--------------------- 5 files changed, 44 insertions(+), 56 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1fab6f..8fcf200 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,6 @@ readme = "README.md" [lib] proc-macro = true -[dependencies] -proc-macro2 = "1.0" - [dev-dependencies] demo-hack = { version = "0.0.5", path = "demo-hack" } demo-hack-impl = { version = "0.0.5", path = "demo-hack-impl" } diff --git a/src/error.rs b/src/error.rs index aef3eff..7334a53 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; +use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; use std::iter::FromIterator; pub struct Error { diff --git a/src/lib.rs b/src/lib.rs index 8edddc8..36430b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,7 +136,7 @@ mod parse; use crate::error::{compile_error, Error}; use crate::parse::*; -use proc_macro2::{token_stream, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; +use proc_macro::{token_stream, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; use std::fmt::Write; use std::iter::Peekable; @@ -169,14 +169,10 @@ struct Macro { } #[proc_macro_attribute] -pub fn proc_macro_hack( - args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let ref mut args = TokenStream::from(args).into_iter().peekable(); - let ref mut input = TokenStream::from(input).into_iter().peekable(); - let output = expand_proc_macro_hack(args, input).unwrap_or_else(compile_error); - proc_macro::TokenStream::from(output) +pub fn proc_macro_hack(args: TokenStream, input: TokenStream) -> TokenStream { + let ref mut args = args.into_iter().peekable(); + let ref mut input = input.into_iter().peekable(); + expand_proc_macro_hack(args, input).unwrap_or_else(compile_error) } fn expand_proc_macro_hack(args: Iter, input: Iter) -> Result { @@ -194,10 +190,9 @@ fn expand_proc_macro_hack(args: Iter, input: Iter) -> Result #[doc(hidden)] #[proc_macro_derive(ProcMacroHack)] -pub fn enum_hack(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let ref mut input = TokenStream::from(input).into_iter().peekable(); - let output = parse_enum_hack(input).unwrap_or_else(compile_error); - proc_macro::TokenStream::from(output) +pub fn enum_hack(input: TokenStream) -> TokenStream { + let ref mut input = input.into_iter().peekable(); + parse_enum_hack(input).unwrap_or_else(compile_error) } struct FakeCallSite { @@ -207,14 +202,10 @@ struct FakeCallSite { #[doc(hidden)] #[proc_macro_attribute] -pub fn fake_call_site( - args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let ref mut args = TokenStream::from(args).into_iter().peekable(); - let ref mut input = TokenStream::from(input).into_iter().peekable(); - let output = expand_fake_call_site(args, input).unwrap_or_else(compile_error); - proc_macro::TokenStream::from(output) +pub fn fake_call_site(args: TokenStream, input: TokenStream) -> TokenStream { + let ref mut args = args.into_iter().peekable(); + let ref mut input = input.into_iter().peekable(); + expand_fake_call_site(args, input).unwrap_or_else(compile_error) } fn expand_fake_call_site(args: Iter, input: Iter) -> Result { diff --git a/src/parse.rs b/src/parse.rs index 322fe49..6bc3a6f 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,15 +1,15 @@ use crate::{Define, Error, Export, ExportArgs, FakeCallSite, Input, Iter, Macro, Visibility}; -use proc_macro2::Delimiter::{Brace, Bracket, Parenthesis}; -use proc_macro2::{token_stream, Delimiter, Ident, Span, TokenStream, TokenTree}; +use proc_macro::Delimiter::{Brace, Bracket, Parenthesis}; +use proc_macro::{token_stream, Delimiter, Ident, Span, TokenStream, TokenTree}; use std::iter::Peekable; pub(crate) fn parse_input(tokens: Iter) -> Result { let attrs = parse_attributes(tokens)?; let vis = parse_visibility(tokens)?; let kw = parse_ident(tokens)?; - if kw == "use" { + if kw.to_string() == "use" { parse_export(attrs, vis, tokens).map(Input::Export) - } else if kw == "fn" { + } else if kw.to_string() == "fn" { parse_define(attrs, vis, kw.span(), tokens).map(Input::Define) } else { Err(Error::new( @@ -86,7 +86,7 @@ fn parse_define( fn parse_macro(tokens: Iter) -> Result { let name = parse_ident(tokens)?; let export_as = match tokens.peek() { - Some(TokenTree::Ident(ident)) if ident == "as" => { + Some(TokenTree::Ident(ident)) if ident.to_string() == "as" => { tokens.next().unwrap(); parse_ident(tokens)? } @@ -107,7 +107,7 @@ fn parse_ident(tokens: Iter) -> Result { fn parse_keyword(tokens: Iter, kw: &'static str) -> Result<(), Error> { match &tokens.next() { - Some(TokenTree::Ident(ident)) if ident == kw => Ok(()), + Some(TokenTree::Ident(ident)) if ident.to_string() == kw => Ok(()), tt => Err(Error::new( tt.as_ref().map_or_else(Span::call_site, TokenTree::span), format!("expected `{}`", kw), @@ -140,7 +140,7 @@ fn parse_group( fn parse_visibility(tokens: Iter) -> Result { if let Some(TokenTree::Ident(ident)) = tokens.peek() { - if ident == "pub" { + if ident.to_string() == "pub" { return Ok(Some(tokens.next().unwrap().span())); } } @@ -174,17 +174,17 @@ pub(crate) fn parse_export_args(tokens: Iter) -> Result { while let Some(tt) = tokens.next() { match &tt { - TokenTree::Ident(ident) if ident == "support_nested" => { + TokenTree::Ident(ident) if ident.to_string() == "support_nested" => { args.support_nested = true; } - TokenTree::Ident(ident) if ident == "internal_macro_calls" => { + TokenTree::Ident(ident) if ident.to_string() == "internal_macro_calls" => { parse_punct(tokens, '=')?; let calls = parse_int(tokens).map_err(|span| { Error::new(span, "expected integer value for internal_macro_calls") })?; args.internal_macro_calls = calls; } - TokenTree::Ident(ident) if ident == "fake_call_site" => { + TokenTree::Ident(ident) if ident.to_string() == "fake_call_site" => { args.fake_call_site = true; } _ => { diff --git a/src/quote.rs b/src/quote.rs index db2ddbe..a3d648d 100644 --- a/src/quote.rs +++ b/src/quote.rs @@ -1,12 +1,12 @@ -use proc_macro2::{Ident, TokenStream, TokenTree}; +use proc_macro::{Ident, TokenStream, TokenTree}; use std::iter; macro_rules! quote { () => { - ::proc_macro2::TokenStream::new() + ::proc_macro::TokenStream::new() }; ($($tt:tt)*) => {{ - let mut tokens = ::proc_macro2::TokenStream::new(); + let mut tokens = ::proc_macro::TokenStream::new(); quote_each_token!(tokens $($tt)*); tokens }}; @@ -18,13 +18,13 @@ macro_rules! quote_each_token { quote_each_token!($tokens $($rest)*); }; ($tokens:ident $ident:ident $($rest:tt)*) => { - <::proc_macro2::TokenStream as ::std::iter::Extend<_>>::extend( + <::proc_macro::TokenStream as ::std::iter::Extend<_>>::extend( &mut $tokens, ::std::iter::once( - ::proc_macro2::TokenTree::Ident( - ::proc_macro2::Ident::new( + ::proc_macro::TokenTree::Ident( + ::proc_macro::Ident::new( stringify!($ident), - ::proc_macro2::Span::call_site(), + ::proc_macro::Span::call_site(), ), ), ), @@ -32,12 +32,12 @@ macro_rules! quote_each_token { quote_each_token!($tokens $($rest)*); }; ($tokens:ident ( $($inner:tt)* ) $($rest:tt)*) => { - <::proc_macro2::TokenStream as ::std::iter::Extend<_>>::extend( + <::proc_macro::TokenStream as ::std::iter::Extend<_>>::extend( &mut $tokens, ::std::iter::once( - ::proc_macro2::TokenTree::Group( - ::proc_macro2::Group::new( - ::proc_macro2::Delimiter::Parenthesis, + ::proc_macro::TokenTree::Group( + ::proc_macro::Group::new( + ::proc_macro::Delimiter::Parenthesis, quote!($($inner)*), ), ), @@ -46,12 +46,12 @@ macro_rules! quote_each_token { quote_each_token!($tokens $($rest)*); }; ($tokens:ident [ $($inner:tt)* ] $($rest:tt)*) => { - <::proc_macro2::TokenStream as ::std::iter::Extend<_>>::extend( + <::proc_macro::TokenStream as ::std::iter::Extend<_>>::extend( &mut $tokens, ::std::iter::once( - ::proc_macro2::TokenTree::Group( - ::proc_macro2::Group::new( - ::proc_macro2::Delimiter::Bracket, + ::proc_macro::TokenTree::Group( + ::proc_macro::Group::new( + ::proc_macro::Delimiter::Bracket, quote!($($inner)*), ), ), @@ -60,12 +60,12 @@ macro_rules! quote_each_token { quote_each_token!($tokens $($rest)*); }; ($tokens:ident { $($inner:tt)* } $($rest:tt)*) => { - <::proc_macro2::TokenStream as ::std::iter::Extend<_>>::extend( + <::proc_macro::TokenStream as ::std::iter::Extend<_>>::extend( &mut $tokens, ::std::iter::once( - ::proc_macro2::TokenTree::Group( - ::proc_macro2::Group::new( - ::proc_macro2::Delimiter::Brace, + ::proc_macro::TokenTree::Group( + ::proc_macro::Group::new( + ::proc_macro::Delimiter::Brace, quote!($($inner)*), ), ), @@ -74,9 +74,9 @@ macro_rules! quote_each_token { quote_each_token!($tokens $($rest)*); }; ($tokens:ident $punct:tt $($rest:tt)*) => { - <::proc_macro2::TokenStream as ::std::iter::Extend<_>>::extend( + <::proc_macro::TokenStream as ::std::iter::Extend<_>>::extend( &mut $tokens, - stringify!($punct).parse::<::proc_macro2::TokenStream>(), + stringify!($punct).parse::<::proc_macro::TokenStream>(), ); quote_each_token!($tokens $($rest)*); }; From 33f48cdca5f284a5d155b9fa0790c9e75d806949 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 15:33:33 -0700 Subject: [PATCH 11/93] Update clippy suppressions --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 36430b8..4e3d826 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,8 +123,7 @@ //! [`proc-macro-nested`]: https://docs.rs/proc-macro-nested #![recursion_limit = "512"] -#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))] -#![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] +#![allow(clippy::needless_doctest_main, clippy::toplevel_ref_arg)] extern crate proc_macro; From c343e522f63d1cc51ee4d234269f34b34e248cb6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 15:36:28 -0700 Subject: [PATCH 12/93] Remove minimal versions check now that there are no dependencies --- .travis.yml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1a5134a..3e6107d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,16 +11,3 @@ rust: script: - cargo run --manifest-path example/Cargo.toml - cargo test --all - -matrix: - include: - - rust: nightly - name: Minimal versions - before_script: - - cargo update -Z minimal-versions --manifest-path example/Cargo.toml - script: - - cargo check --manifest-path example/Cargo.toml - - rust: 1.31.0 - name: 2015 edition - script: - - cargo check From 30a2aaf5821f167db504940fbec42c31ef32bf1b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 15:37:55 -0700 Subject: [PATCH 13/93] Release 0.5.13 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8fcf200..0e61949 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-hack" -version = "0.5.12" +version = "0.5.13" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" From fec550cdabab767c9834022fa63ef2cce1c2a35b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 16:44:50 -0700 Subject: [PATCH 14/93] Fix parsing of braced re-exports --- src/parse.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parse.rs b/src/parse.rs index 6bc3a6f..c6b1665 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -40,6 +40,7 @@ fn parse_export(attrs: TokenStream, vis: Visibility, tokens: Iter) -> Result macros.push(parse_macro(tokens)?), } From 1f255cf745d1ad537e232e4625a548f59c7f4f92 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 Mar 2020 16:46:03 -0700 Subject: [PATCH 15/93] Release 0.5.14 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0e61949..debfb65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-hack" -version = "0.5.13" +version = "0.5.14" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" From 5e9a3da706e1fd8a348df8dd55ce9911ebeffa5b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 31 Mar 2020 08:20:58 -0700 Subject: [PATCH 16/93] Suppress dead_code lint on enum hack --- src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 4e3d826..292ac74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -320,6 +320,7 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { macro_rules! #export_as { ($($proc_macro:tt)*) => {{ #do_derive + #[allow(dead_code)] enum ProcMacroHack { #enum_variant = (stringify! { $($proc_macro)* }, 0).1, } @@ -353,6 +354,8 @@ fn expand_define(define: Define) -> TokenStream { let mut iter = input.into_iter(); iter.next().unwrap(); // `enum` iter.next().unwrap(); // `ProcMacroHack` + iter.next().unwrap(); // `#` + iter.next().unwrap(); // `[allow(dead_code)]` let mut braces = match iter.next().unwrap() { #dummy::TokenTree::Group(group) => group.stream().into_iter(), From 29f01e8f33414f20a953dd1363277ac5d1cf7974 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 31 Mar 2020 08:26:01 -0700 Subject: [PATCH 17/93] Release 0.5.15 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index debfb65..3076694 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-hack" -version = "0.5.14" +version = "0.5.15" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" From 14db9296f902fc20e5b96e0cb563dd1a42d3873a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 May 2020 21:04:29 -0700 Subject: [PATCH 18/93] Remove CI badge from Cargo.toml Support for badges has been deprecated by crates.io. --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3076694..0e28bb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,9 +20,6 @@ rustversion = "1.0" syn = "1.0.5" trybuild = "1.0" -[badges] -travis-ci = { repository = "dtolnay/proc-macro-hack" } - [workspace] members = ["demo-hack", "demo-hack-impl", "example", "nested"] From c1f196a403a043837a5ca4d99236de6b5ca7b77f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 3 May 2020 00:09:23 -0700 Subject: [PATCH 19/93] Enable GitHub Actions --- .github/workflows/ci.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e188e1d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,22 @@ +name: test + +on: + push: + pull_request: + schedule: [cron: "40 1 * * *"] + +jobs: + test: + name: Rust ${{matrix.rust}} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rust: [nightly, beta, stable, 1.31.0] + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + - run: cargo run --manifest-path example/Cargo.toml + - run: cargo test --all From 5780dee32b4310d07f001886debfcf88d39f01db Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 3 May 2020 23:34:17 -0700 Subject: [PATCH 20/93] Remove Travis configuration --- .github/workflows/ci.yml | 2 +- .travis.yml | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e188e1d..fea412f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: test +name: CI on: push: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3e6107d..0000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -sudo: false - -language: rust - -rust: - - nightly - - beta - - stable - - 1.31.0 - -script: - - cargo run --manifest-path example/Cargo.toml - - cargo test --all From 740f7795595c85b8c14145bf32defb4845021fe7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 6 May 2020 00:11:13 -0700 Subject: [PATCH 21/93] Update build status badge to GitHub Actions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7984e6d..1449af0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Procedural macros in expression position ======================================== -[![Build Status](https://api.travis-ci.org/dtolnay/proc-macro-hack.svg?branch=master)](https://travis-ci.org/dtolnay/proc-macro-hack) +[![Build Status](https://img.shields.io/github/workflow/status/dtolnay/proc-macro-hack/CI/master)](https://github.com/dtolnay/proc-macro-hack/actions?query=branch%3Amaster) [![Latest Version](https://img.shields.io/crates/v/proc-macro-hack.svg)](https://crates.io/crates/proc-macro-hack) As of Rust 1.30, the language supports user-defined function-like procedural From 476d25c42c779df5d3726d187d79ab0b61fa031e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 24 May 2020 14:45:06 -0700 Subject: [PATCH 22/93] Tweak links to not look like quoted text --- README.md | 6 +++--- src/lib.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1449af0..6461f24 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Two crates are required to define a procedural macro. This crate must contain nothing but procedural macros. Private helper functions and private modules are fine but nothing can be public. -[> example of an implementation crate][demo-hack-impl] +[» example of an implementation crate][demo-hack-impl] Just like you would use a #\[proc_macro\] attribute to define a natively supported procedural macro, use proc-macro-hack's #\[proc_macro_hack\] @@ -52,7 +52,7 @@ pub fn add_one(input: TokenStream) -> TokenStream { This crate is allowed to contain other public things if you need, for example traits or functions or ordinary macros. -[> example of a declaration crate][demo-hack] +[» example of a declaration crate][demo-hack] Within the declaration crate there needs to be a re-export of your procedural macro from the implementation crate. The re-export also carries a @@ -88,7 +88,7 @@ proc-macro = true Users of your crate depend on your declaration crate (not your implementation crate), then use your procedural macros as usual. -[> example of a downstream crate][example] +[» example of a downstream crate][example] ```rust use demo_hack::add_one; diff --git a/src/lib.rs b/src/lib.rs index 292ac74..a9d7e8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ //! This crate must contain nothing but procedural macros. Private helper //! functions and private modules are fine but nothing can be public. //! -//! [> example of an implementation crate][demo-hack-impl] +//! [» example of an implementation crate][demo-hack-impl] //! //! Just like you would use a #\[proc_macro\] attribute to define a natively //! supported procedural macro, use proc-macro-hack's #\[proc_macro_hack\] @@ -48,7 +48,7 @@ //! This crate is allowed to contain other public things if you need, for //! example traits or functions or ordinary macros. //! -//! [> example of a declaration crate][demo-hack] +//! [» example of a declaration crate][demo-hack] //! //! Within the declaration crate there needs to be a re-export of your //! procedural macro from the implementation crate. The re-export also carries a @@ -86,7 +86,7 @@ //! Users of your crate depend on your declaration crate (not your //! implementation crate), then use your procedural macros as usual. //! -//! [> example of a downstream crate][example] +//! [» example of a downstream crate][example] //! //! ``` //! use demo_hack::add_one; From 7bdb83bef1b14d424e0834cedab7deb272fa5f67 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 24 May 2020 14:47:40 -0700 Subject: [PATCH 23/93] Expand glob import --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a9d7e8b..6f91ac1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,7 +134,9 @@ mod error; mod parse; use crate::error::{compile_error, Error}; -use crate::parse::*; +use crate::parse::{ + parse_define_args, parse_enum_hack, parse_export_args, parse_fake_call_site, parse_input, +}; use proc_macro::{token_stream, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; use std::fmt::Write; use std::iter::Peekable; From 091e1365128a5537ded8e266d0cba9c515bf2bce Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 24 May 2020 15:00:04 -0700 Subject: [PATCH 24/93] Wrap token iterator in a new type This is in prepration for building better behavior into it, such as transparently stepping into None-delimited groups. --- src/iter.rs | 28 ++++++++++++++++++++++++++++ src/lib.rs | 16 ++++++++-------- src/parse.rs | 15 ++++++--------- 3 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 src/iter.rs diff --git a/src/iter.rs b/src/iter.rs new file mode 100644 index 0000000..c25be0b --- /dev/null +++ b/src/iter.rs @@ -0,0 +1,28 @@ +use proc_macro::{token_stream, TokenStream, TokenTree}; +use std::iter::Peekable; + +pub type Iter<'a> = &'a mut IterImpl; + +pub struct IterImpl { + tokens: Peekable, +} + +pub fn new(tokens: TokenStream) -> IterImpl { + IterImpl { + tokens: tokens.into_iter().peekable(), + } +} + +impl IterImpl { + pub fn peek(&mut self) -> Option<&TokenTree> { + self.tokens.peek() + } +} + +impl Iterator for IterImpl { + type Item = TokenTree; + + fn next(&mut self) -> Option { + self.tokens.next() + } +} diff --git a/src/lib.rs b/src/lib.rs index 6f91ac1..4dc5d00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,17 +131,17 @@ extern crate proc_macro; mod quote; mod error; +mod iter; mod parse; use crate::error::{compile_error, Error}; +use crate::iter::Iter; use crate::parse::{ parse_define_args, parse_enum_hack, parse_export_args, parse_fake_call_site, parse_input, }; -use proc_macro::{token_stream, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; +use proc_macro::{Ident, Punct, Spacing, Span, TokenStream, TokenTree}; use std::fmt::Write; -use std::iter::Peekable; -type Iter<'a> = &'a mut Peekable; type Visibility = Option; enum Input { @@ -171,8 +171,8 @@ struct Macro { #[proc_macro_attribute] pub fn proc_macro_hack(args: TokenStream, input: TokenStream) -> TokenStream { - let ref mut args = args.into_iter().peekable(); - let ref mut input = input.into_iter().peekable(); + let ref mut args = iter::new(args); + let ref mut input = iter::new(input); expand_proc_macro_hack(args, input).unwrap_or_else(compile_error) } @@ -192,7 +192,7 @@ fn expand_proc_macro_hack(args: Iter, input: Iter) -> Result #[doc(hidden)] #[proc_macro_derive(ProcMacroHack)] pub fn enum_hack(input: TokenStream) -> TokenStream { - let ref mut input = input.into_iter().peekable(); + let ref mut input = iter::new(input); parse_enum_hack(input).unwrap_or_else(compile_error) } @@ -204,8 +204,8 @@ struct FakeCallSite { #[doc(hidden)] #[proc_macro_attribute] pub fn fake_call_site(args: TokenStream, input: TokenStream) -> TokenStream { - let ref mut args = args.into_iter().peekable(); - let ref mut input = input.into_iter().peekable(); + let ref mut args = iter::new(args); + let ref mut input = iter::new(input); expand_fake_call_site(args, input).unwrap_or_else(compile_error) } diff --git a/src/parse.rs b/src/parse.rs index c6b1665..de7c599 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,7 +1,7 @@ -use crate::{Define, Error, Export, ExportArgs, FakeCallSite, Input, Iter, Macro, Visibility}; +use crate::iter::{self, Iter, IterImpl}; +use crate::{Define, Error, Export, ExportArgs, FakeCallSite, Input, Macro, Visibility}; use proc_macro::Delimiter::{Brace, Bracket, Parenthesis}; -use proc_macro::{token_stream, Delimiter, Ident, Span, TokenStream, TokenTree}; -use std::iter::Peekable; +use proc_macro::{Delimiter, Ident, Span, TokenStream, TokenTree}; pub(crate) fn parse_input(tokens: Iter) -> Result { let attrs = parse_attributes(tokens)?; @@ -29,7 +29,7 @@ fn parse_export(attrs: TokenStream, vis: Visibility, tokens: Iter) -> Result { - let ref mut content = group.stream().into_iter().peekable(); + let ref mut content = iter::new(group.stream()); loop { macros.push(parse_macro(content)?); if content.peek().is_none() { @@ -124,13 +124,10 @@ fn parse_int(tokens: Iter) -> Result { } } -fn parse_group( - tokens: Iter, - delimiter: Delimiter, -) -> Result, Error> { +fn parse_group(tokens: Iter, delimiter: Delimiter) -> Result { match &tokens.next() { Some(TokenTree::Group(group)) if group.delimiter() == delimiter => { - Ok(group.stream().into_iter().peekable()) + Ok(iter::new(group.stream())) } tt => Err(Error::new( tt.as_ref().map_or_else(Span::call_site, TokenTree::span), From 51a988bc02d8f872e8d77bdb70848e3a27de00ba Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 24 May 2020 15:15:10 -0700 Subject: [PATCH 25/93] Traverse into None-delimited groups --- src/iter.rs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/iter.rs b/src/iter.rs index c25be0b..722013c 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,21 +1,23 @@ -use proc_macro::{token_stream, TokenStream, TokenTree}; -use std::iter::Peekable; +use proc_macro::{token_stream, Delimiter, TokenStream, TokenTree}; pub type Iter<'a> = &'a mut IterImpl; pub struct IterImpl { - tokens: Peekable, + stack: Vec, + peeked: Option, } pub fn new(tokens: TokenStream) -> IterImpl { IterImpl { - tokens: tokens.into_iter().peekable(), + stack: vec![tokens.into_iter()], + peeked: None, } } impl IterImpl { pub fn peek(&mut self) -> Option<&TokenTree> { - self.tokens.peek() + self.peeked = self.next(); + self.peeked.as_ref() } } @@ -23,6 +25,18 @@ impl Iterator for IterImpl { type Item = TokenTree; fn next(&mut self) -> Option { - self.tokens.next() + if let Some(tt) = self.peeked.take() { + return Some(tt); + } + loop { + let top = self.stack.last_mut()?; + match top.next() { + None => drop(self.stack.pop()), + Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::None => { + self.stack.push(group.stream().into_iter()); + } + Some(tt) => return Some(tt), + } + } } } From 442391cc323fa1c867a32c11431b575cf85cf958 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 24 May 2020 15:41:55 -0700 Subject: [PATCH 26/93] Add crosslink icons to top of rustdoc --- src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 4dc5d00..16d7823 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,11 @@ +//! [![github]](https://github.com/dtolnay/proc-macro-hack) [![crates-io]](https://crates.io/crates/proc-macro-hack) [![docs-rs]](https://docs.rs/proc-macro-hack) +//! +//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github +//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K +//! +//!
+//! //! As of Rust 1.30, the language supports user-defined function-like procedural //! macros. However these can only be invoked in item position, not in //! statements or expressions. From be8a69630e38ce22c96fc8cda8e771f3a2dec96c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 24 May 2020 15:42:48 -0700 Subject: [PATCH 27/93] Try out a new style of readme badges --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6461f24..d6a6463 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ Procedural macros in expression position ======================================== -[![Build Status](https://img.shields.io/github/workflow/status/dtolnay/proc-macro-hack/CI/master)](https://github.com/dtolnay/proc-macro-hack/actions?query=branch%3Amaster) -[![Latest Version](https://img.shields.io/crates/v/proc-macro-hack.svg)](https://crates.io/crates/proc-macro-hack) +[github](https://github.com/dtolnay/proc-macro-hack) +[crates.io](https://crates.io/crates/proc-macro-hack) +[docs.rs](https://docs.rs/proc-macro-hack) +[build status](https://github.com/dtolnay/proc-macro-hack/actions?query=branch%3Amaster) As of Rust 1.30, the language supports user-defined function-like procedural macros. However these can only be invoked in item position, not in From 35b1fa620a6486ed4b544d4fec6aa8cbdd017bd6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 24 May 2020 15:46:10 -0700 Subject: [PATCH 28/93] Release 0.5.16 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0e28bb9..1f75add 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-hack" -version = "0.5.15" +version = "0.5.16" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" From 531e51267996cacc0afc1a542d009f4b0ecc29b6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 25 May 2020 12:52:36 -0700 Subject: [PATCH 29/93] Convert export expansion to loop --- src/lib.rs | 141 ++++++++++++++++++++++++++--------------------------- 1 file changed, 69 insertions(+), 72 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 16d7823..7754e06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -262,84 +262,81 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { }; let from = export.from; - let rules = export - .macros - .into_iter() - .map(|Macro { name, export_as }| { - let actual_name = actual_proc_macro_name(&name); - let dispatch = dispatch_macro_name(&name); - let call_site = call_site_macro_name(&name); - - let export_dispatch = if args.support_nested { - quote! { - #[doc(hidden)] - #vis use proc_macro_nested::dispatch as #dispatch; - } - } else { - quote!() - }; + let mut rules = TokenStream::new(); + for Macro { name, export_as } in export.macros { + let actual_name = actual_proc_macro_name(&name); + let dispatch = dispatch_macro_name(&name); + let call_site = call_site_macro_name(&name); - let proc_macro_call = if args.support_nested { - let extra_bangs = (0..args.internal_macro_calls) - .map(|_| TokenTree::Punct(Punct::new('!', Spacing::Alone))) - .collect::(); - quote! { - #crate_prefix #dispatch! { ($($proc_macro)*) #extra_bangs } - } - } else { - quote! { - proc_macro_call!() - } - }; - - let export_call_site = if args.fake_call_site { - quote! { - #[doc(hidden)] - #vis use proc_macro_hack::fake_call_site as #call_site; - } - } else { - quote!() - }; + let export_dispatch = if args.support_nested { + quote! { + #[doc(hidden)] + #vis use proc_macro_nested::dispatch as #dispatch; + } + } else { + quote!() + }; - let do_derive = if !args.fake_call_site { - quote! { - #[derive(#crate_prefix #actual_name)] - } - } else if crate_prefix.is_some() { - quote! { - use #crate_prefix #actual_name; - #[#crate_prefix #call_site ($($proc_macro)*)] - #[derive(#actual_name)] - } - } else { - quote! { - #[#call_site ($($proc_macro)*)] - #[derive(#actual_name)] - } - }; + let proc_macro_call = if args.support_nested { + let extra_bangs = (0..args.internal_macro_calls) + .map(|_| TokenTree::Punct(Punct::new('!', Spacing::Alone))) + .collect::(); + quote! { + #crate_prefix #dispatch! { ($($proc_macro)*) #extra_bangs } + } + } else { + quote! { + proc_macro_call!() + } + }; + let export_call_site = if args.fake_call_site { quote! { #[doc(hidden)] - #vis use #from::#actual_name; - - #export_dispatch - #export_call_site - - #attrs - #macro_export - macro_rules! #export_as { - ($($proc_macro:tt)*) => {{ - #do_derive - #[allow(dead_code)] - enum ProcMacroHack { - #enum_variant = (stringify! { $($proc_macro)* }, 0).1, - } - #proc_macro_call - }}; - } + #vis use proc_macro_hack::fake_call_site as #call_site; + } + } else { + quote!() + }; + + let do_derive = if !args.fake_call_site { + quote! { + #[derive(#crate_prefix #actual_name)] } - }) - .collect(); + } else if crate_prefix.is_some() { + quote! { + use #crate_prefix #actual_name; + #[#crate_prefix #call_site ($($proc_macro)*)] + #[derive(#actual_name)] + } + } else { + quote! { + #[#call_site ($($proc_macro)*)] + #[derive(#actual_name)] + } + }; + + rules.extend(quote! { + #[doc(hidden)] + #vis use #from::#actual_name; + + #export_dispatch + #export_call_site + + #attrs + #macro_export + macro_rules! #export_as { + ($($proc_macro:tt)*) => {{ + #do_derive + #[allow(dead_code)] + enum ProcMacroHack { + #enum_variant = (stringify! { $($proc_macro)* }, 0).1, + } + #proc_macro_call + }}; + } + }); + } wrap_in_enum_hack(dummy, rules) } From 74f71a0ba180e948acd7506de63aec7c68fec86a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 25 May 2020 13:02:08 -0700 Subject: [PATCH 30/93] Join exports into one `use` --- src/lib.rs | 94 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 36 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7754e06..8bb22d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -262,42 +262,29 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { }; let from = export.from; - let mut rules = TokenStream::new(); + let mut actual_names = TokenStream::new(); + let mut export_dispatch = TokenStream::new(); + let mut export_call_site = TokenStream::new(); + let mut macro_rules = TokenStream::new(); for Macro { name, export_as } in export.macros { let actual_name = actual_proc_macro_name(&name); let dispatch = dispatch_macro_name(&name); let call_site = call_site_macro_name(&name); - let export_dispatch = if args.support_nested { - quote! { - #[doc(hidden)] - #vis use proc_macro_nested::dispatch as #dispatch; - } - } else { - quote!() - }; + if !actual_names.is_empty() { + actual_names.extend(quote!(,)); + } + actual_names.extend(quote!(#actual_name)); - let proc_macro_call = if args.support_nested { - let extra_bangs = (0..args.internal_macro_calls) - .map(|_| TokenTree::Punct(Punct::new('!', Spacing::Alone))) - .collect::(); - quote! { - #crate_prefix #dispatch! { ($($proc_macro)*) #extra_bangs } - } - } else { - quote! { - proc_macro_call!() - } - }; + if !export_dispatch.is_empty() { + export_dispatch.extend(quote!(,)); + } + export_dispatch.extend(quote!(dispatch as #dispatch)); - let export_call_site = if args.fake_call_site { - quote! { - #[doc(hidden)] - #vis use proc_macro_hack::fake_call_site as #call_site; - } - } else { - quote!() - }; + if !export_call_site.is_empty() { + export_call_site.extend(quote!(,)); + } + export_call_site.extend(quote!(fake_call_site as #call_site)); let do_derive = if !args.fake_call_site { quote! { @@ -316,13 +303,20 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { } }; - rules.extend(quote! { - #[doc(hidden)] - #vis use #from::#actual_name; - - #export_dispatch - #export_call_site + let proc_macro_call = if args.support_nested { + let extra_bangs = (0..args.internal_macro_calls) + .map(|_| TokenTree::Punct(Punct::new('!', Spacing::Alone))) + .collect::(); + quote! { + #crate_prefix #dispatch! { ($($proc_macro)*) #extra_bangs } + } + } else { + quote! { + proc_macro_call!() + } + }; + macro_rules.extend(quote! { #attrs #macro_export macro_rules! #export_as { @@ -338,7 +332,35 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { }); } - wrap_in_enum_hack(dummy, rules) + let export_dispatch = if args.support_nested { + quote! { + #[doc(hidden)] + #vis use proc_macro_nested::{#export_dispatch}; + } + } else { + quote!() + }; + + let export_call_site = if args.fake_call_site { + quote! { + #[doc(hidden)] + #vis use proc_macro_hack::{#export_call_site}; + } + } else { + quote!() + }; + + let expanded = quote! { + #[doc(hidden)] + #vis use #from::{#actual_names}; + + #export_dispatch + #export_call_site + + #macro_rules + }; + + wrap_in_enum_hack(dummy, expanded) } fn expand_define(define: Define) -> TokenStream { From 9fea5855a5edce776a039f6743b8c87ec8128fbc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 25 May 2020 13:04:03 -0700 Subject: [PATCH 31/93] Avoid unneeded braces in `use` --- src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8bb22d2..b6cde26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -266,7 +266,7 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let mut export_dispatch = TokenStream::new(); let mut export_call_site = TokenStream::new(); let mut macro_rules = TokenStream::new(); - for Macro { name, export_as } in export.macros { + for Macro { name, export_as } in &export.macros { let actual_name = actual_proc_macro_name(&name); let dispatch = dispatch_macro_name(&name); let call_site = call_site_macro_name(&name); @@ -332,10 +332,16 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { }); } + if export.macros.len() != 1 { + export_dispatch = quote!({#export_dispatch}); + export_call_site = quote!({#export_call_site}); + actual_names = quote!({#actual_names}); + } + let export_dispatch = if args.support_nested { quote! { #[doc(hidden)] - #vis use proc_macro_nested::{#export_dispatch}; + #vis use proc_macro_nested::#export_dispatch; } } else { quote!() @@ -344,7 +350,7 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let export_call_site = if args.fake_call_site { quote! { #[doc(hidden)] - #vis use proc_macro_hack::{#export_call_site}; + #vis use proc_macro_hack::#export_call_site; } } else { quote!() @@ -352,7 +358,7 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let expanded = quote! { #[doc(hidden)] - #vis use #from::{#actual_names}; + #vis use #from::#actual_names; #export_dispatch #export_call_site From b928826d4c246ad0f186ec2b644f2ad01a85221c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 25 May 2020 13:07:48 -0700 Subject: [PATCH 32/93] Allow macro calls anywhere in declaration crate --- build.rs | 30 ++++++++++++++++++++++++++++++ src/lib.rs | 12 ++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..d383837 --- /dev/null +++ b/build.rs @@ -0,0 +1,30 @@ +use std::env; +use std::process::Command; +use std::str; + +fn main() { + let compiler = match rustc_version() { + Some(compiler) => compiler, + None => return, + }; + + if compiler.minor < 33 { + println!("cargo:rustc-cfg=proc_macro_hack_no_crate_as_underscore"); + } +} + +struct Compiler { + minor: u32, +} + +fn rustc_version() -> Option { + let rustc = env::var_os("RUSTC")?; + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = str::from_utf8(&output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + let minor = pieces.next()?.parse().ok()?; + Some(Compiler { minor }) +} diff --git a/src/lib.rs b/src/lib.rs index b6cde26..8c83622 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -332,6 +332,16 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { }); } + let extern_crate = if cfg!(proc_macro_hack_no_crate_as_underscore) { + quote!() + } else { + quote! { + #[allow(unused_imports)] + #[macro_use] + extern crate #from as _; + } + }; + if export.macros.len() != 1 { export_dispatch = quote!({#export_dispatch}); export_call_site = quote!({#export_call_site}); @@ -357,6 +367,8 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { }; let expanded = quote! { + #extern_crate + #[doc(hidden)] #vis use #from::#actual_names; From a7793198b95ae03e60035e095b72aac4254e8a0d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 25 May 2020 13:11:57 -0700 Subject: [PATCH 33/93] Release 0.5.17 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1f75add..4eabee2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-hack" -version = "0.5.16" +version = "0.5.17" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" From 769b04169b68b17cd1a4b7d4cdd9b869e6f789f4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 25 May 2020 13:23:28 -0700 Subject: [PATCH 34/93] Revert "Allow macro calls anywhere in declaration crate" If a module contained more than one separate `use` from the same implementation crate: error: `proc_macro_hack_macroname` is already in scope --> src/lib.rs:10:1 | 10 | #[proc_macro_hack] | ^^^^^^^^^^^^^^^^^^ | = note: macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) This reverts commit b928826d4c246ad0f186ec2b644f2ad01a85221c. --- build.rs | 30 ------------------------------ src/lib.rs | 12 ------------ 2 files changed, 42 deletions(-) delete mode 100644 build.rs diff --git a/build.rs b/build.rs deleted file mode 100644 index d383837..0000000 --- a/build.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::env; -use std::process::Command; -use std::str; - -fn main() { - let compiler = match rustc_version() { - Some(compiler) => compiler, - None => return, - }; - - if compiler.minor < 33 { - println!("cargo:rustc-cfg=proc_macro_hack_no_crate_as_underscore"); - } -} - -struct Compiler { - minor: u32, -} - -fn rustc_version() -> Option { - let rustc = env::var_os("RUSTC")?; - let output = Command::new(rustc).arg("--version").output().ok()?; - let version = str::from_utf8(&output.stdout).ok()?; - let mut pieces = version.split('.'); - if pieces.next() != Some("rustc 1") { - return None; - } - let minor = pieces.next()?.parse().ok()?; - Some(Compiler { minor }) -} diff --git a/src/lib.rs b/src/lib.rs index 8c83622..b6cde26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -332,16 +332,6 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { }); } - let extern_crate = if cfg!(proc_macro_hack_no_crate_as_underscore) { - quote!() - } else { - quote! { - #[allow(unused_imports)] - #[macro_use] - extern crate #from as _; - } - }; - if export.macros.len() != 1 { export_dispatch = quote!({#export_dispatch}); export_call_site = quote!({#export_call_site}); @@ -367,8 +357,6 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { }; let expanded = quote! { - #extern_crate - #[doc(hidden)] #vis use #from::#actual_names; From af66eddc4887f568532e8e7997cd20b841636c90 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 4 Jun 2020 22:10:54 -0700 Subject: [PATCH 35/93] More concise statement of version support --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d6a6463..2ed1179 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ statements or expressions. This crate implements an alternative type of procedural macro that can be invoked in statement or expression position. -This approach works with any stable or nightly Rust version 1.31+. +This approach works with any Rust version 1.31+. ## Defining procedural macros From 7f73f06a093c72c315a177fea0c4328cd2e82d68 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 7 Jun 2020 13:55:38 -0700 Subject: [PATCH 36/93] Add rerun-if-changed to proc-macro-nested build script --- nested/build.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nested/build.rs b/nested/build.rs index b4aec63..e08addc 100644 --- a/nested/build.rs +++ b/nested/build.rs @@ -16,6 +16,9 @@ macro_rules! count { */ fn main() { + // Tell Cargo not to rerun on src/lib.rs changes. + println!("cargo:rerun-if-changed=build.rs"); + let out_dir = env::var("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("count.rs"); let mut f = File::create(&dest_path).unwrap(); From 3bc2adc4c78fa66e6d6310f13939473c7a86dea4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 7 Jun 2020 13:56:41 -0700 Subject: [PATCH 37/93] Release proc-macro-nested 0.1.5 --- nested/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nested/Cargo.toml b/nested/Cargo.toml index a6d323f..0d6bc81 100644 --- a/nested/Cargo.toml +++ b/nested/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-nested" -version = "0.1.4" +version = "0.1.5" authors = ["David Tolnay "] license = "MIT OR Apache-2.0" description = "Support for nested proc-macro-hack invocations" From 3dd7561eace18fe63d9885b14342a6d773e00213 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 13 Jun 2020 18:14:32 -0700 Subject: [PATCH 38/93] Simplify build script using fs::write --- nested/build.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nested/build.rs b/nested/build.rs index e08addc..d67e7ad 100644 --- a/nested/build.rs +++ b/nested/build.rs @@ -1,6 +1,5 @@ use std::env; -use std::fs::File; -use std::io::Write; +use std::fs; use std::iter; use std::path::Path; @@ -21,7 +20,6 @@ fn main() { let out_dir = env::var("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("count.rs"); - let mut f = File::create(&dest_path).unwrap(); let mut content = String::new(); content += " @@ -40,5 +38,5 @@ fn main() { } "; - f.write_all(content.as_bytes()).unwrap(); + fs::write(dest_path, content).unwrap(); } From 689af3233a66c9c17847775144e2c7f7c2e6a679 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 13 Jun 2020 18:15:29 -0700 Subject: [PATCH 39/93] Avoid bumping count.rs filetime if content is up to date --- nested/build.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/nested/build.rs b/nested/build.rs index d67e7ad..c5a6fee 100644 --- a/nested/build.rs +++ b/nested/build.rs @@ -18,9 +18,6 @@ fn main() { // Tell Cargo not to rerun on src/lib.rs changes. println!("cargo:rerun-if-changed=build.rs"); - let out_dir = env::var("OUT_DIR").unwrap(); - let dest_path = Path::new(&out_dir).join("count.rs"); - let mut content = String::new(); content += " #[doc(hidden)] @@ -38,5 +35,16 @@ fn main() { } "; - fs::write(dest_path, content).unwrap(); + let content = content.as_bytes(); + let out_dir = env::var("OUT_DIR").unwrap(); + let ref dest_path = Path::new(&out_dir).join("count.rs"); + + // Avoid bumping filetime if content is up to date. Possibly related to + // https://github.com/dtolnay/proc-macro-hack/issues/56 ...? + if fs::read(dest_path) + .map(|existing| existing != content) + .unwrap_or(true) + { + fs::write(dest_path, content).unwrap(); + } } From 6c89d167e3b07dc4df08e610f61b086ec6357647 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 13 Jun 2020 18:20:22 -0700 Subject: [PATCH 40/93] Generate formatted count.rs for easier examination --- nested/build.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/nested/build.rs b/nested/build.rs index c5a6fee..ef56f8e 100644 --- a/nested/build.rs +++ b/nested/build.rs @@ -19,21 +19,17 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); let mut content = String::new(); - content += " - #[doc(hidden)] - #[macro_export] - macro_rules! count { - "; + content += "#[doc(hidden)]\n"; + content += "#[macro_export]\n"; + content += "macro_rules! count {\n"; for i in 0..=64 { let bangs = iter::repeat("!").take(i).collect::(); - content += &format!("({}) => {{ proc_macro_call_{}!() }};", bangs, i); + content += &format!(" ({}) => {{ proc_macro_call_{}!() }};\n", bangs, i); } - content += " - ($(!)+) => { - compile_error!(\"this macro does not support >64 nested macro invocations\") - }; - } - "; + content += " ($(!)+) => {\n"; + content += " compile_error! { \"this macro does not support >64 nested macro invocations\" }\n"; + content += " };\n"; + content += "}\n"; let content = content.as_bytes(); let out_dir = env::var("OUT_DIR").unwrap(); From 8a3cec3cfc7b8417ef8cc2970f7de27ff2b6a023 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 13 Jun 2020 18:21:18 -0700 Subject: [PATCH 41/93] Release proc-macro-nested 0.1.6 --- nested/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nested/Cargo.toml b/nested/Cargo.toml index 0d6bc81..fd0fa99 100644 --- a/nested/Cargo.toml +++ b/nested/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-nested" -version = "0.1.5" +version = "0.1.6" authors = ["David Tolnay "] license = "MIT OR Apache-2.0" description = "Support for nested proc-macro-hack invocations" From 9e08476044fc77b0ad10230481c1e0d0579dd98c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 14 Jun 2020 13:20:32 -0700 Subject: [PATCH 42/93] Add a sponsors link --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..7507077 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: dtolnay From 6790ad4abcbf0539d0a09caa60fb97ea0a4be804 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 25 Jul 2020 16:26:15 -0700 Subject: [PATCH 43/93] Remove 'extern crate proc_macro' from example code --- .github/workflows/ci.yml | 4 +++- README.md | 2 -- demo-hack-impl/src/lib.rs | 2 -- src/lib.rs | 4 ++-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fea412f..ca24eac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,11 +12,13 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, beta, stable, 1.31.0] + rust: [nightly, beta, stable, 1.42.0, 1.31.0] steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} + - if: matrix.rust == '1.31.0' + run: echo 'extern crate proc_macro;' >> demo-hack-impl/src/lib.rs - run: cargo run --manifest-path example/Cargo.toml - run: cargo test --all diff --git a/README.md b/README.md index 2ed1179..104ed7c 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,6 @@ The function signature is the same as for ordinary function-like procedural macros. ```rust -extern crate proc_macro; - use proc_macro::TokenStream; use proc_macro_hack::proc_macro_hack; use quote::quote; diff --git a/demo-hack-impl/src/lib.rs b/demo-hack-impl/src/lib.rs index ecfed2c..8d0f4b0 100644 --- a/demo-hack-impl/src/lib.rs +++ b/demo-hack-impl/src/lib.rs @@ -1,5 +1,3 @@ -extern crate proc_macro; - use proc_macro::TokenStream; use proc_macro_hack::proc_macro_hack; use quote::quote; diff --git a/src/lib.rs b/src/lib.rs index b6cde26..352e837 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,8 +31,8 @@ //! macros. //! //! ``` -//! extern crate proc_macro; -//! +//! # extern crate proc_macro; +//! # //! use proc_macro::TokenStream; //! use proc_macro_hack::proc_macro_hack; //! use quote::quote; From a3b681b369f60a39de5c9f8f71a26f871a913510 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 25 Jul 2020 23:23:31 -0700 Subject: [PATCH 44/93] Note that 1.45 proc macros support expr position natively --- README.md | 12 +++++++++--- src/lib.rs | 8 +++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 104ed7c..df7a2ec 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,15 @@ Procedural macros in expression position [docs.rs](https://docs.rs/proc-macro-hack) [build status](https://github.com/dtolnay/proc-macro-hack/actions?query=branch%3Amaster) -As of Rust 1.30, the language supports user-defined function-like procedural -macros. However these can only be invoked in item position, not in -statements or expressions. +

+Note: As of Rust 1.45 this crate is superseded by native support for +#[proc_macro] in expression position. Only consider using this crate if you care +about supporting compilers between 1.31 and 1.45. +
+ +Since Rust 1.30, the language supports user-defined function-like procedural +macros. However these can only be invoked in item position, not in statements or +expressions. This crate implements an alternative type of procedural macro that can be invoked in statement or expression position. diff --git a/src/lib.rs b/src/lib.rs index 352e837..901a719 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,13 @@ //! //!
//! -//! As of Rust 1.30, the language supports user-defined function-like procedural +//!

+//! Note: As of Rust 1.45 this crate is superseded by native support +//! for #[proc_macro] in expression position. Only consider using this crate if +//! you care about supporting compilers between 1.31 and 1.45. +//!
+//! +//! Since Rust 1.30, the language supports user-defined function-like procedural //! macros. However these can only be invoked in item position, not in //! statements or expressions. //! From 3da1be8f9519c725b1f425a954b06a1a0a8b25ac Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 25 Jul 2020 23:37:42 -0700 Subject: [PATCH 45/93] Release 0.5.18 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4eabee2..153d880 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-hack" -version = "0.5.17" +version = "0.5.18" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" From 9e68d2484577fd39c860f129118094fc49469a19 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 14:45:25 -0700 Subject: [PATCH 46/93] Wrap long line in proc-macro-nested build script --- nested/build.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nested/build.rs b/nested/build.rs index ef56f8e..d451b74 100644 --- a/nested/build.rs +++ b/nested/build.rs @@ -27,7 +27,9 @@ fn main() { content += &format!(" ({}) => {{ proc_macro_call_{}!() }};\n", bangs, i); } content += " ($(!)+) => {\n"; - content += " compile_error! { \"this macro does not support >64 nested macro invocations\" }\n"; + content += " compile_error! {\n"; + content += " \"this macro does not support >64 nested macro invocations\"\n"; + content += " }\n"; content += " };\n"; content += "}\n"; From eadb0254626ab853e2e1176a7648c8c3795bfeb9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 14:51:49 -0700 Subject: [PATCH 47/93] Format with rustfmt 2.0.0-rc.2 --- src/parse.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parse.rs b/src/parse.rs index de7c599..5539dea 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -189,7 +189,7 @@ pub(crate) fn parse_export_args(tokens: Iter) -> Result { return Err(Error::new( tt.span(), "expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`", - )) + )); } } if tokens.peek().is_none() { From c73e24d509c41647248532cfd86cf7337d46bb66 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 14:39:01 -0700 Subject: [PATCH 48/93] Add ui test of unexpected args on macro definition --- tests/ui/unexpected-arg.rs | 8 ++++++++ tests/ui/unexpected-arg.stderr | 7 +++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/ui/unexpected-arg.rs create mode 100644 tests/ui/unexpected-arg.stderr diff --git a/tests/ui/unexpected-arg.rs b/tests/ui/unexpected-arg.rs new file mode 100644 index 0000000..376fc0d --- /dev/null +++ b/tests/ui/unexpected-arg.rs @@ -0,0 +1,8 @@ +use proc_macro_hack::proc_macro_hack; + +#[proc_macro_hack(fake_call_site)] +pub fn my_macro(input: TokenStream) -> TokenStream { + unimplemented!() +} + +fn main() {} diff --git a/tests/ui/unexpected-arg.stderr b/tests/ui/unexpected-arg.stderr new file mode 100644 index 0000000..785b0c4 --- /dev/null +++ b/tests/ui/unexpected-arg.stderr @@ -0,0 +1,7 @@ +error: unexpected input + --> $DIR/unexpected-arg.rs:3:1 + | +3 | #[proc_macro_hack(fake_call_site)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) From dc246ed5d3dc8b9bf152c851f6aa680886121a86 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 14:40:58 -0700 Subject: [PATCH 49/93] Improve unexpected argument error --- src/parse.rs | 10 ++++++---- tests/ui/unexpected-arg.stderr | 8 +++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/parse.rs b/src/parse.rs index 5539dea..7d98a43 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -202,10 +202,12 @@ pub(crate) fn parse_export_args(tokens: Iter) -> Result { } pub(crate) fn parse_define_args(tokens: Iter) -> Result<(), Error> { - if tokens.peek().is_none() { - Ok(()) - } else { - Err(Error::new(Span::call_site(), "unexpected input")) + match tokens.peek() { + None => Ok(()), + Some(token) => Err(Error::new( + token.span(), + "unexpected argument to proc_macro_hack macro implementation; args are only accepted on the macro declaration (the `pub use`)", + )), } } diff --git a/tests/ui/unexpected-arg.stderr b/tests/ui/unexpected-arg.stderr index 785b0c4..6895e82 100644 --- a/tests/ui/unexpected-arg.stderr +++ b/tests/ui/unexpected-arg.stderr @@ -1,7 +1,5 @@ -error: unexpected input - --> $DIR/unexpected-arg.rs:3:1 +error: unexpected argument to proc_macro_hack macro implementation; args are only accepted on the macro declaration (the `pub use`) + --> $DIR/unexpected-arg.rs:3:19 | 3 | #[proc_macro_hack(fake_call_site)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^^^^^^^^^^^ From 0ba7439fb540608cb0b9e0ee95d992c51c5ca1b1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 16:08:03 -0700 Subject: [PATCH 50/93] Avoid having to recreate a new pub token --- src/lib.rs | 4 ++-- src/parse.rs | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 901a719..6ce1b1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,7 +156,7 @@ use crate::parse::{ use proc_macro::{Ident, Punct, Spacing, Span, TokenStream, TokenTree}; use std::fmt::Write; -type Visibility = Option; +type Visibility = Option; enum Input { Export(Export), @@ -250,7 +250,7 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let dummy = dummy_name_for_export(&export); let attrs = export.attrs; - let ref vis = export.vis.map(|span| Ident::new("pub", span)); + let vis = export.vis; let macro_export = match vis { Some(_) => quote!(#[macro_export]), None => quote!(), diff --git a/src/parse.rs b/src/parse.rs index 7d98a43..de51fd2 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -139,7 +139,10 @@ fn parse_group(tokens: Iter, delimiter: Delimiter) -> Result { fn parse_visibility(tokens: Iter) -> Result { if let Some(TokenTree::Ident(ident)) = tokens.peek() { if ident.to_string() == "pub" { - return Ok(Some(tokens.next().unwrap().span())); + match tokens.next().unwrap() { + TokenTree::Ident(vis) => return Ok(Some(vis)), + _ => unreachable!(), + } } } Ok(None) From c194c0b2c873eb6f6f6cfa4ec3d77936a6e536c7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 16:21:21 -0700 Subject: [PATCH 51/93] Clarify that 'actual name' is the pub macro in implementation crate --- src/lib.rs | 72 +++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6ce1b1f..0231fdb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -273,14 +273,14 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let mut export_call_site = TokenStream::new(); let mut macro_rules = TokenStream::new(); for Macro { name, export_as } in &export.macros { - let actual_name = actual_proc_macro_name(&name); + let pub_name = pub_proc_macro_name(&name); let dispatch = dispatch_macro_name(&name); let call_site = call_site_macro_name(&name); if !actual_names.is_empty() { actual_names.extend(quote!(,)); } - actual_names.extend(quote!(#actual_name)); + actual_names.extend(quote!(#pub_name)); if !export_dispatch.is_empty() { export_dispatch.extend(quote!(,)); @@ -294,18 +294,18 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let do_derive = if !args.fake_call_site { quote! { - #[derive(#crate_prefix #actual_name)] + #[derive(#crate_prefix #pub_name)] } } else if crate_prefix.is_some() { quote! { - use #crate_prefix #actual_name; + use #crate_prefix #pub_name; #[#crate_prefix #call_site ($($proc_macro)*)] - #[derive(#actual_name)] + #[derive(#pub_name)] } } else { quote! { #[#call_site ($($proc_macro)*)] - #[derive(#actual_name)] + #[derive(#pub_name)] } }; @@ -378,18 +378,18 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { fn expand_define(define: Define) -> TokenStream { let attrs = define.attrs; let name = define.name; - let dummy = actual_proc_macro_name(&name); + let pub_name = pub_proc_macro_name(&name); let body = define.body; quote! { - mod #dummy { + mod #pub_name { extern crate proc_macro; pub use self::proc_macro::*; } #attrs - #[proc_macro_derive(#dummy)] - pub fn #dummy(input: #dummy::TokenStream) -> #dummy::TokenStream { + #[proc_macro_derive(#pub_name)] + pub fn #pub_name(input: #pub_name::TokenStream) -> #pub_name::TokenStream { use std::iter::FromIterator; let mut iter = input.into_iter(); @@ -399,7 +399,7 @@ fn expand_define(define: Define) -> TokenStream { iter.next().unwrap(); // `[allow(dead_code)]` let mut braces = match iter.next().unwrap() { - #dummy::TokenTree::Group(group) => group.stream().into_iter(), + #pub_name::TokenTree::Group(group) => group.stream().into_iter(), _ => unimplemented!(), }; let variant = braces.next().unwrap(); // `Value` or `Nested` @@ -408,29 +408,29 @@ fn expand_define(define: Define) -> TokenStream { braces.next().unwrap(); // `=` let mut parens = match braces.next().unwrap() { - #dummy::TokenTree::Group(group) => group.stream().into_iter(), + #pub_name::TokenTree::Group(group) => group.stream().into_iter(), _ => unimplemented!(), }; parens.next().unwrap(); // `stringify` parens.next().unwrap(); // `!` let inner = match parens.next().unwrap() { - #dummy::TokenTree::Group(group) => group.stream(), + #pub_name::TokenTree::Group(group) => group.stream(), _ => unimplemented!(), }; - let output: #dummy::TokenStream = #name(inner.clone()); + let output: #pub_name::TokenStream = #name(inner.clone()); - fn count_bangs(input: #dummy::TokenStream) -> usize { + fn count_bangs(input: #pub_name::TokenStream) -> usize { let mut count = 0; for token in input { match token { - #dummy::TokenTree::Punct(punct) => { + #pub_name::TokenTree::Punct(punct) => { if punct.as_char() == '!' { count += 1; } } - #dummy::TokenTree::Group(group) => { + #pub_name::TokenTree::Group(group) => { count += count_bangs(group.stream()); } _ => {} @@ -442,15 +442,15 @@ fn expand_define(define: Define) -> TokenStream { // macro_rules! proc_macro_call { // () => { #output } // } - #dummy::TokenStream::from_iter(vec![ - #dummy::TokenTree::Ident( - #dummy::Ident::new("macro_rules", #dummy::Span::call_site()), + #pub_name::TokenStream::from_iter(vec![ + #pub_name::TokenTree::Ident( + #pub_name::Ident::new("macro_rules", #pub_name::Span::call_site()), ), - #dummy::TokenTree::Punct( - #dummy::Punct::new('!', #dummy::Spacing::Alone), + #pub_name::TokenTree::Punct( + #pub_name::Punct::new('!', #pub_name::Spacing::Alone), ), - #dummy::TokenTree::Ident( - #dummy::Ident::new( + #pub_name::TokenTree::Ident( + #pub_name::Ident::new( &if support_nested { let extra_bangs = if varname == "Nested" { 0 @@ -461,22 +461,22 @@ fn expand_define(define: Define) -> TokenStream { } else { String::from("proc_macro_call") }, - #dummy::Span::call_site(), + #pub_name::Span::call_site(), ), ), - #dummy::TokenTree::Group( - #dummy::Group::new(#dummy::Delimiter::Brace, #dummy::TokenStream::from_iter(vec![ - #dummy::TokenTree::Group( - #dummy::Group::new(#dummy::Delimiter::Parenthesis, #dummy::TokenStream::new()), + #pub_name::TokenTree::Group( + #pub_name::Group::new(#pub_name::Delimiter::Brace, #pub_name::TokenStream::from_iter(vec![ + #pub_name::TokenTree::Group( + #pub_name::Group::new(#pub_name::Delimiter::Parenthesis, #pub_name::TokenStream::new()), ), - #dummy::TokenTree::Punct( - #dummy::Punct::new('=', #dummy::Spacing::Joint), + #pub_name::TokenTree::Punct( + #pub_name::Punct::new('=', #pub_name::Spacing::Joint), ), - #dummy::TokenTree::Punct( - #dummy::Punct::new('>', #dummy::Spacing::Alone), + #pub_name::TokenTree::Punct( + #pub_name::Punct::new('>', #pub_name::Spacing::Alone), ), - #dummy::TokenTree::Group( - #dummy::Group::new(#dummy::Delimiter::Brace, output), + #pub_name::TokenTree::Group( + #pub_name::Group::new(#pub_name::Delimiter::Brace, output), ), ])), ), @@ -487,7 +487,7 @@ fn expand_define(define: Define) -> TokenStream { } } -fn actual_proc_macro_name(conceptual: &Ident) -> Ident { +fn pub_proc_macro_name(conceptual: &Ident) -> Ident { Ident::new( &format!("proc_macro_hack_{}", conceptual), conceptual.span(), From 5cc836b52081d8cf89e3eaddda658b250c1b7f39 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 14:56:50 -0700 Subject: [PATCH 52/93] Parse only_hack_old_rustc arg --- src/lib.rs | 1 + src/parse.rs | 6 +++++- tests/ui/unknown-arg.stderr | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0231fdb..d3ab533 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -244,6 +244,7 @@ struct ExportArgs { support_nested: bool, internal_macro_calls: u16, fake_call_site: bool, + only_hack_old_rustc: bool, } fn expand_export(export: Export, args: ExportArgs) -> TokenStream { diff --git a/src/parse.rs b/src/parse.rs index de51fd2..fe074f1 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -171,6 +171,7 @@ pub(crate) fn parse_export_args(tokens: Iter) -> Result { support_nested: false, internal_macro_calls: 0, fake_call_site: false, + only_hack_old_rustc: false, }; while let Some(tt) = tokens.next() { @@ -188,10 +189,13 @@ pub(crate) fn parse_export_args(tokens: Iter) -> Result { TokenTree::Ident(ident) if ident.to_string() == "fake_call_site" => { args.fake_call_site = true; } + TokenTree::Ident(ident) if ident.to_string() == "only_hack_old_rustc" => { + args.only_hack_old_rustc = true; + } _ => { return Err(Error::new( tt.span(), - "expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`", + "expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`, `only_hack_old_rustc`", )); } } diff --git a/tests/ui/unknown-arg.stderr b/tests/ui/unknown-arg.stderr index d3cd8c9..ffe3705 100644 --- a/tests/ui/unknown-arg.stderr +++ b/tests/ui/unknown-arg.stderr @@ -1,4 +1,4 @@ -error: expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site` +error: expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`, `only_hack_old_rustc` --> $DIR/unknown-arg.rs:3:35 | 3 | #[proc_macro_hack(fake_call_site, support_nexted)] From 26dc7014116498c4bd6fa1b14a737c376f0fc002 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 26 Oct 2020 13:13:49 -0700 Subject: [PATCH 53/93] Detect rustc support for native proc macro --- build.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6ab4098 --- /dev/null +++ b/build.rs @@ -0,0 +1,31 @@ +use std::env; +use std::process::Command; +use std::str; + +// The rustc-cfg strings below are *not* public API. Please let us know by +// opening a GitHub issue if your build environment requires some way to enable +// these cfgs other than by executing our build script. +fn main() { + let minor = match rustc_minor_version() { + Some(minor) => minor, + None => return, + }; + + // Function-like procedural macros in expressions, patterns, and statements + // stabilized in Rust 1.45: + // https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#stabilizing-function-like-procedural-macros-in-expressions-patterns-and-statements + if minor < 45 { + println!("cargo:rustc-cfg=need_proc_macro_hack"); + } +} + +fn rustc_minor_version() -> Option { + let rustc = env::var_os("RUSTC")?; + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = str::from_utf8(&output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + pieces.next()?.parse().ok() +} From 11e5ad645844ea107ae36e6ba38ec34b661f5790 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 15:54:18 -0700 Subject: [PATCH 54/93] Also export the unmodified proc macro behavior --- src/lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index d3ab533..ef71809 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -380,6 +380,7 @@ fn expand_define(define: Define) -> TokenStream { let attrs = define.attrs; let name = define.name; let pub_name = pub_proc_macro_name(&name); + let nohack = original_proc_macro_name(&name); let body = define.body; quote! { @@ -484,6 +485,13 @@ fn expand_define(define: Define) -> TokenStream { ]) } + #attrs + #[doc(hidden)] + #[proc_macro] + pub fn #nohack(input: #pub_name::TokenStream) -> #pub_name::TokenStream { + #name(input) + } + fn #name #body } } @@ -495,6 +503,13 @@ fn pub_proc_macro_name(conceptual: &Ident) -> Ident { ) } +fn original_proc_macro_name(conceptual: &Ident) -> Ident { + Ident::new( + &format!("proc_macro_nohack_{}", conceptual), + conceptual.span(), + ) +} + fn dispatch_macro_name(conceptual: &Ident) -> Ident { Ident::new( &format!("proc_macro_call_{}", conceptual), From f6407e0f92fc5ce75dba85104b88c28dcc3a5599 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 16:31:47 -0700 Subject: [PATCH 55/93] Switch public name to the one that new rustc can re-export directly --- src/lib.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ef71809..27c914c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -274,14 +274,14 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let mut export_call_site = TokenStream::new(); let mut macro_rules = TokenStream::new(); for Macro { name, export_as } in &export.macros { - let pub_name = pub_proc_macro_name(&name); + let hacked = hacked_proc_macro_name(&name); let dispatch = dispatch_macro_name(&name); let call_site = call_site_macro_name(&name); if !actual_names.is_empty() { actual_names.extend(quote!(,)); } - actual_names.extend(quote!(#pub_name)); + actual_names.extend(quote!(#hacked)); if !export_dispatch.is_empty() { export_dispatch.extend(quote!(,)); @@ -295,18 +295,18 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let do_derive = if !args.fake_call_site { quote! { - #[derive(#crate_prefix #pub_name)] + #[derive(#crate_prefix #hacked)] } } else if crate_prefix.is_some() { quote! { - use #crate_prefix #pub_name; + use #crate_prefix #hacked; #[#crate_prefix #call_site ($($proc_macro)*)] - #[derive(#pub_name)] + #[derive(#hacked)] } } else { quote! { #[#call_site ($($proc_macro)*)] - #[derive(#pub_name)] + #[derive(#hacked)] } }; @@ -380,7 +380,7 @@ fn expand_define(define: Define) -> TokenStream { let attrs = define.attrs; let name = define.name; let pub_name = pub_proc_macro_name(&name); - let nohack = original_proc_macro_name(&name); + let hacked = hacked_proc_macro_name(&name); let body = define.body; quote! { @@ -390,8 +390,9 @@ fn expand_define(define: Define) -> TokenStream { } #attrs - #[proc_macro_derive(#pub_name)] - pub fn #pub_name(input: #pub_name::TokenStream) -> #pub_name::TokenStream { + #[doc(hidden)] + #[proc_macro_derive(#hacked)] + pub fn #hacked(input: #pub_name::TokenStream) -> #pub_name::TokenStream { use std::iter::FromIterator; let mut iter = input.into_iter(); @@ -486,9 +487,8 @@ fn expand_define(define: Define) -> TokenStream { } #attrs - #[doc(hidden)] #[proc_macro] - pub fn #nohack(input: #pub_name::TokenStream) -> #pub_name::TokenStream { + pub fn #pub_name(input: #pub_name::TokenStream) -> #pub_name::TokenStream { #name(input) } @@ -503,9 +503,9 @@ fn pub_proc_macro_name(conceptual: &Ident) -> Ident { ) } -fn original_proc_macro_name(conceptual: &Ident) -> Ident { +fn hacked_proc_macro_name(conceptual: &Ident) -> Ident { Ident::new( - &format!("proc_macro_nohack_{}", conceptual), + &format!("_proc_macro_hack_{}", conceptual), conceptual.span(), ) } From a6107565dc80e90040a5f133603fa4a14caf4a93 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 16:05:49 -0700 Subject: [PATCH 56/93] Expand to direct re-export on new rustc if only_hack_old_rustc --- src/lib.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 27c914c..2a06815 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -248,6 +248,10 @@ struct ExportArgs { } fn expand_export(export: Export, args: ExportArgs) -> TokenStream { + if args.only_hack_old_rustc && cfg!(not(need_proc_macro_hack)) { + return expand_export_nohack(export); + } + let dummy = dummy_name_for_export(&export); let attrs = export.attrs; @@ -376,6 +380,30 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { wrap_in_enum_hack(dummy, expanded) } +fn expand_export_nohack(export: Export) -> TokenStream { + let attrs = export.attrs; + let vis = export.vis; + let from = export.from; + let mut names = TokenStream::new(); + + for Macro { name, export_as } in &export.macros { + let pub_name = pub_proc_macro_name(&name); + if !names.is_empty() { + names.extend(quote!(,)); + } + names.extend(quote!(#pub_name as #export_as)); + } + + if export.macros.len() != 1 { + names = quote!({#names}); + } + + quote! { + #attrs + #vis use #from::#names; + } +} + fn expand_define(define: Define) -> TokenStream { let attrs = define.attrs; let name = define.name; From a991a7eca28f2730384173b9b18bfd87496a7f64 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 16:38:36 -0700 Subject: [PATCH 57/93] Add CI coverage of only_hack_old_rustc --- .github/workflows/ci.yml | 2 +- demo-hack/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca24eac..c4d0fa9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, beta, stable, 1.42.0, 1.31.0] + rust: [nightly, beta, stable, 1.45.0, 1.42.0, 1.31.0] steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@master diff --git a/demo-hack/src/lib.rs b/demo-hack/src/lib.rs index 0eebdc9..f37c8f9 100644 --- a/demo-hack/src/lib.rs +++ b/demo-hack/src/lib.rs @@ -3,5 +3,5 @@ use proc_macro_hack::proc_macro_hack; /// Add one to an expression. /// /// (Documentation goes here on the re-export, not in the other crate.) -#[proc_macro_hack] +#[proc_macro_hack(only_hack_old_rustc)] pub use demo_hack_impl::add_one; From d686d312d85a1cb041fc2615fd512f59a6b40415 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 16:46:42 -0700 Subject: [PATCH 58/93] Document only_hack_old_rustc --- README.md | 7 +++++++ src/lib.rs | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/README.md b/README.md index df7a2ec..c734710 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,13 @@ fn main() { macro input, use `#[proc_macro_hack(fake_call_site)]` on the re-export in your declaration crate. *Most macros won't need this.* +- On compilers that are new enough to natively support proc macros in expression + position, proc-macro-hack does not automatically use that support, since the + hygiene can be subtly different between the two implementations. To opt in to + compiling your macro to native `#[proc_macro]` on sufficiently new compilers, + use `#[proc_macro_hack(only_hack_old_rustc)]` on the re-export in your + declaration crate. + [#10]: https://github.com/dtolnay/proc-macro-hack/issues/10 [#20]: https://github.com/dtolnay/proc-macro-hack/issues/20 [`proc-macro-nested`]: https://docs.rs/proc-macro-nested diff --git a/src/lib.rs b/src/lib.rs index 2a06815..90b04fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,6 +132,14 @@ //! in the macro input, use `#[proc_macro_hack(fake_call_site)]` on the //! re-export in your declaration crate. *Most macros won't need this.* //! +//! - On compilers that are new enough to natively support proc macros in +//! expression position, proc-macro-hack does not automatically use that +//! support, since the hygiene can be subtly different between the two +//! implementations. To opt in to compiling your macro to native +//! `#[proc_macro]` on sufficiently new compilers, use +//! `#[proc_macro_hack(only_hack_old_rustc)]` on the re-export in your +//! declaration crate. +//! //! [#10]: https://github.com/dtolnay/proc-macro-hack/issues/10 //! [#20]: https://github.com/dtolnay/proc-macro-hack/issues/20 //! [`proc-macro-nested`]: https://docs.rs/proc-macro-nested From 758784bb05c3d68569ca5001972fe5c31b8175c5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Oct 2020 17:02:02 -0700 Subject: [PATCH 59/93] Release 0.5.19 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 153d880..61150b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-hack" -version = "0.5.18" +version = "0.5.19" authors = ["David Tolnay "] edition = "2018" license = "MIT OR Apache-2.0" From ba711b02d6f78fc29e10011b2d34b0f464237b1f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 31 Dec 2020 00:45:09 -0800 Subject: [PATCH 60/93] Run clippy linter in CI --- .github/workflows/ci.yml | 8 ++++++++ src/lib.rs | 7 ++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4d0fa9..33d7352 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,3 +22,11 @@ jobs: run: echo 'extern crate proc_macro;' >> demo-hack-impl/src/lib.rs - run: cargo run --manifest-path example/Cargo.toml - run: cargo test --all + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@clippy + - run: cargo clippy --tests -- -Dclippy::all diff --git a/src/lib.rs b/src/lib.rs index 90b04fd..8f20c14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,7 +145,12 @@ //! [`proc-macro-nested`]: https://docs.rs/proc-macro-nested #![recursion_limit = "512"] -#![allow(clippy::needless_doctest_main, clippy::toplevel_ref_arg)] +#![allow( + clippy::manual_strip, + clippy::needless_doctest_main, + clippy::toplevel_ref_arg, + clippy::unnecessary_wraps +)] extern crate proc_macro; From f4ccf0e4e50de4c63d6aa0fdcd3df8d46e27a0be Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 31 Dec 2020 00:46:41 -0800 Subject: [PATCH 61/93] Resolve unnecessary_wraps clippy lint --- src/lib.rs | 3 +-- src/parse.rs | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8f20c14..652fb85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -148,8 +148,7 @@ #![allow( clippy::manual_strip, clippy::needless_doctest_main, - clippy::toplevel_ref_arg, - clippy::unnecessary_wraps + clippy::toplevel_ref_arg )] extern crate proc_macro; diff --git a/src/parse.rs b/src/parse.rs index fe074f1..f9c3b80 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -5,7 +5,7 @@ use proc_macro::{Delimiter, Ident, Span, TokenStream, TokenTree}; pub(crate) fn parse_input(tokens: Iter) -> Result { let attrs = parse_attributes(tokens)?; - let vis = parse_visibility(tokens)?; + let vis = parse_visibility(tokens); let kw = parse_ident(tokens)?; if kw.to_string() == "use" { parse_export(attrs, vis, tokens).map(Input::Export) @@ -136,16 +136,16 @@ fn parse_group(tokens: Iter, delimiter: Delimiter) -> Result { } } -fn parse_visibility(tokens: Iter) -> Result { +fn parse_visibility(tokens: Iter) -> Visibility { if let Some(TokenTree::Ident(ident)) = tokens.peek() { if ident.to_string() == "pub" { match tokens.next().unwrap() { - TokenTree::Ident(vis) => return Ok(Some(vis)), + TokenTree::Ident(vis) => return Some(vis), _ => unreachable!(), } } } - Ok(None) + None } fn parse_attributes(tokens: Iter) -> Result { From 10bb830de3ef7f8315514979d2cb789970c97b36 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 31 Dec 2020 00:47:24 -0800 Subject: [PATCH 62/93] Opt in to pedantic clippy lints --- .github/workflows/ci.yml | 2 +- src/lib.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33d7352..9720ea8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,4 +29,4 @@ jobs: steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@clippy - - run: cargo clippy --tests -- -Dclippy::all + - run: cargo clippy --tests -- -Dclippy::pedantic diff --git a/src/lib.rs b/src/lib.rs index 652fb85..17391e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,8 +146,12 @@ #![recursion_limit = "512"] #![allow( + clippy::doc_markdown, clippy::manual_strip, + clippy::module_name_repetitions, clippy::needless_doctest_main, + clippy::needless_pass_by_value, + clippy::too_many_lines, clippy::toplevel_ref_arg )] From c01123bdf7811705a3d2d1f589123f034a9b229a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 31 Dec 2020 00:47:40 -0800 Subject: [PATCH 63/93] Inform clippy of supported compiler version in clippy.toml --- .clippy.toml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .clippy.toml diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 0000000..3d30690 --- /dev/null +++ b/.clippy.toml @@ -0,0 +1 @@ +msrv = "1.31.0" From bccfc64736fa0676db38f3d577489509f748c521 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 4 Jan 2021 23:49:04 -0800 Subject: [PATCH 64/93] Fix catching clippy warnings as CI failures --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9720ea8..55a562f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,4 +29,4 @@ jobs: steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@clippy - - run: cargo clippy --tests -- -Dclippy::pedantic + - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic From 89bdd2641429e739168eb0e68d8ef3cadd96064b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 13 Jan 2021 11:42:12 -0800 Subject: [PATCH 65/93] Add windows builder in CI --- .github/workflows/ci.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55a562f..54e6973 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,12 +7,17 @@ on: jobs: test: - name: Rust ${{matrix.rust}} - runs-on: ubuntu-latest + name: ${{matrix.name || format('Rust {0}', matrix.rust)}} + runs-on: ${{matrix.os || 'ubuntu'}}-latest strategy: fail-fast: false matrix: - rust: [nightly, beta, stable, 1.45.0, 1.42.0, 1.31.0] + rust: [beta, stable, 1.45.0, 1.42.0, 1.31.0] + include: + - rust: nightly + - rust: nightly + name: Windows + os: windows steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@master From fe92ebc8d9867ee2e4595e0252991b0cd28e7f6a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 13 Jan 2021 13:55:19 -0800 Subject: [PATCH 66/93] Work around UNC path join --- nested/build.rs | 4 +++- nested/src/lib.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nested/build.rs b/nested/build.rs index d451b74..01dbc0d 100644 --- a/nested/build.rs +++ b/nested/build.rs @@ -1,7 +1,7 @@ use std::env; use std::fs; use std::iter; -use std::path::Path; +use std::path::{self, Path}; /* #[doc(hidden)] @@ -45,4 +45,6 @@ fn main() { { fs::write(dest_path, content).unwrap(); } + + println!("cargo:rustc-env=PATH_SEPARATOR={}", path::MAIN_SEPARATOR); } diff --git a/nested/src/lib.rs b/nested/src/lib.rs index 0cd8302..0a8a675 100644 --- a/nested/src/lib.rs +++ b/nested/src/lib.rs @@ -40,7 +40,7 @@ #![no_std] -include!(concat!(env!("OUT_DIR"), "/count.rs")); +include!(concat!(env!("OUT_DIR"), env!("PATH_SEPARATOR"), "count.rs")); #[doc(hidden)] #[macro_export] From 0d6c7a078176d77e3fc8911c00bf4bb06d3ba9bd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 13 Jan 2021 14:18:53 -0800 Subject: [PATCH 67/93] Release proc-macro-nested 0.1.7 --- nested/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nested/Cargo.toml b/nested/Cargo.toml index fd0fa99..aa306db 100644 --- a/nested/Cargo.toml +++ b/nested/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-nested" -version = "0.1.6" +version = "0.1.7" authors = ["David Tolnay "] license = "MIT OR Apache-2.0" description = "Support for nested proc-macro-hack invocations" From dc9bbc40689fa5af8c852048e424a224efa76110 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 18 Feb 2021 19:28:19 -0800 Subject: [PATCH 68/93] Ignore let_underscore_drop pedantic clippy lint --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 17391e5..671231e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -147,6 +147,7 @@ #![recursion_limit = "512"] #![allow( clippy::doc_markdown, + clippy::let_underscore_drop, clippy::manual_strip, clippy::module_name_repetitions, clippy::needless_doctest_main, From d8069c65eb546c63730ca73217ab0800c8b52581 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 4 Jun 2021 20:28:10 -0700 Subject: [PATCH 69/93] Resolve needless_borrow clippy lints error: this expression borrows a reference (`&proc_macro::Ident`) that is immediately dereferenced by the compiler --> src/lib.rs:298:45 | 298 | let hacked = hacked_proc_macro_name(&name); | ^^^^^ help: change this to: `name` | = note: `-D clippy::needless-borrow` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow error: this expression borrows a reference (`&proc_macro::Ident`) that is immediately dereferenced by the compiler --> src/lib.rs:299:44 | 299 | let dispatch = dispatch_macro_name(&name); | ^^^^^ help: change this to: `name` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow error: this expression borrows a reference (`&proc_macro::Ident`) that is immediately dereferenced by the compiler --> src/lib.rs:300:46 | 300 | let call_site = call_site_macro_name(&name); | ^^^^^ help: change this to: `name` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow error: this expression borrows a reference (`&proc_macro::Ident`) that is immediately dereferenced by the compiler --> src/lib.rs:407:44 | 407 | let pub_name = pub_proc_macro_name(&name); | ^^^^^ help: change this to: `name` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 671231e..e616498 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -295,9 +295,9 @@ fn expand_export(export: Export, args: ExportArgs) -> TokenStream { let mut export_call_site = TokenStream::new(); let mut macro_rules = TokenStream::new(); for Macro { name, export_as } in &export.macros { - let hacked = hacked_proc_macro_name(&name); - let dispatch = dispatch_macro_name(&name); - let call_site = call_site_macro_name(&name); + let hacked = hacked_proc_macro_name(name); + let dispatch = dispatch_macro_name(name); + let call_site = call_site_macro_name(name); if !actual_names.is_empty() { actual_names.extend(quote!(,)); @@ -404,7 +404,7 @@ fn expand_export_nohack(export: Export) -> TokenStream { let mut names = TokenStream::new(); for Macro { name, export_as } in &export.macros { - let pub_name = pub_proc_macro_name(&name); + let pub_name = pub_proc_macro_name(name); if !names.is_empty() { names.extend(quote!(,)); } From bca911aa72a1419be8d93c63ec587a217fefcd13 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 2 Jul 2021 20:59:51 -0700 Subject: [PATCH 70/93] Ignore buggy nonstandard_macro_braces clippy lint Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422 error: use of irregular braces for `vec!` macro --> demo-hack-impl/src/lib.rs:6:1 | 6 | #[proc_macro_hack] | ^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::nonstandard-macro-braces` implied by `-D clippy::all` help: consider writing `#[proc_macro_hack]` --> demo-hack-impl/src/lib.rs:6:1 | 6 | #[proc_macro_hack] | ^^^^^^^^^^^^^^^^^^ = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces = note: this error originates in the attribute macro `proc_macro_hack` (in Nightly builds, run with -Z macro-backtrace for more info) --- demo-hack-impl/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/demo-hack-impl/src/lib.rs b/demo-hack-impl/src/lib.rs index 8d0f4b0..80b2a41 100644 --- a/demo-hack-impl/src/lib.rs +++ b/demo-hack-impl/src/lib.rs @@ -1,3 +1,8 @@ +#![allow( + // Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422 + clippy::nonstandard_macro_braces, +)] + use proc_macro::TokenStream; use proc_macro_hack::proc_macro_hack; use quote::quote; From 2f10e9de5ce68e9b04687a24b4ebe01bfc27e70e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 7 Aug 2021 20:38:43 -0700 Subject: [PATCH 71/93] Raise minimum tested compiler to rustc 1.34 --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54e6973..aada932 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, stable, 1.45.0, 1.42.0, 1.31.0] + rust: [beta, stable, 1.45.0, 1.42.0, 1.34.0, 1.31.0] include: - rust: nightly - rust: nightly @@ -23,10 +23,11 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} - - if: matrix.rust == '1.31.0' + - if: matrix.rust == '1.31.0' || matrix.rust == '1.34.0' run: echo 'extern crate proc_macro;' >> demo-hack-impl/src/lib.rs - run: cargo run --manifest-path example/Cargo.toml - - run: cargo test --all + - if: matrix.rust != '1.31.0' + run: cargo test --all clippy: name: Clippy From 9a41ef1e834332f95c20c8f8e23ec1913e593125 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 18 Sep 2021 16:36:35 -0700 Subject: [PATCH 72/93] Skip clippy job on pull requests --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aada932..98cbf9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,7 @@ jobs: clippy: name: Clippy runs-on: ubuntu-latest + if: github.event_name != 'pull_request' steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@clippy From 39b540419f19e79a7ec48070bb7f5e4a9ec606a6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 2 Oct 2021 02:16:46 -0400 Subject: [PATCH 73/93] Declare minimum Rust version in Cargo metadata --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 61150b4..cde0b6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "proc-macro-hack" version = "0.5.19" authors = ["David Tolnay "] edition = "2018" +rust-version = "1.31" license = "MIT OR Apache-2.0" description = "Procedural macros in expression position" repository = "https://github.com/dtolnay/proc-macro-hack" From 4231d34bb9a168fdf743a1e898033677a7566ae8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 7 Oct 2021 00:46:13 -0400 Subject: [PATCH 74/93] Update ui test files --- tests/ui/private.stderr | 2 +- tests/ui/unexpected-arg.stderr | 2 +- tests/ui/unexpected.stderr | 2 +- tests/ui/unknown-arg.stderr | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/private.stderr b/tests/ui/private.stderr index be79443..c4f4c5f 100644 --- a/tests/ui/private.stderr +++ b/tests/ui/private.stderr @@ -1,5 +1,5 @@ error: functions tagged with `#[proc_macro_hack]` must be `pub` - --> $DIR/private.rs:4:1 + --> $DIR/tests/ui/private.rs:4:1 | 4 | fn my_macro(input: TokenStream) -> TokenStream { | ^^ diff --git a/tests/ui/unexpected-arg.stderr b/tests/ui/unexpected-arg.stderr index 6895e82..1243ead 100644 --- a/tests/ui/unexpected-arg.stderr +++ b/tests/ui/unexpected-arg.stderr @@ -1,5 +1,5 @@ error: unexpected argument to proc_macro_hack macro implementation; args are only accepted on the macro declaration (the `pub use`) - --> $DIR/unexpected-arg.rs:3:19 + --> $DIR/tests/ui/unexpected-arg.rs:3:19 | 3 | #[proc_macro_hack(fake_call_site)] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/unexpected.stderr b/tests/ui/unexpected.stderr index e3da97a..afe2bd4 100644 --- a/tests/ui/unexpected.stderr +++ b/tests/ui/unexpected.stderr @@ -1,5 +1,5 @@ error: unexpected input to #[proc_macro_hack] - --> $DIR/unexpected.rs:4:5 + --> $DIR/tests/ui/unexpected.rs:4:5 | 4 | pub struct What; | ^^^^^^ diff --git a/tests/ui/unknown-arg.stderr b/tests/ui/unknown-arg.stderr index ffe3705..2f685c0 100644 --- a/tests/ui/unknown-arg.stderr +++ b/tests/ui/unknown-arg.stderr @@ -1,5 +1,5 @@ error: expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`, `only_hack_old_rustc` - --> $DIR/unknown-arg.rs:3:35 + --> $DIR/tests/ui/unknown-arg.rs:3:35 | 3 | #[proc_macro_hack(fake_call_site, support_nexted)] | ^^^^^^^^^^^^^^ From 35813d46ec5e0326dd157857511686698e76280b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 8 Oct 2021 01:50:20 -0400 Subject: [PATCH 75/93] Add actions job to notice outdated dependencies --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98cbf9a..940bd74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,3 +37,11 @@ jobs: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@clippy - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic + + outdated: + name: Outdated + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + steps: + - uses: actions/checkout@v2 + - run: cargo outdated --exit-code 1 From 98ef3c69fcd1b3143a0d3ae67655fc8f182627fb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 8 Oct 2021 02:51:09 -0400 Subject: [PATCH 76/93] Ui test changes for trybuild 1.0.49 --- Cargo.toml | 2 +- tests/ui/private.stderr | 2 +- tests/ui/unexpected-arg.stderr | 2 +- tests/ui/unexpected.stderr | 2 +- tests/ui/unknown-arg.stderr | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cde0b6e..cfa8285 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ demo-hack-impl = { version = "0.0.5", path = "demo-hack-impl" } quote = "1.0" rustversion = "1.0" syn = "1.0.5" -trybuild = "1.0" +trybuild = "1.0.49" [workspace] members = ["demo-hack", "demo-hack-impl", "example", "nested"] diff --git a/tests/ui/private.stderr b/tests/ui/private.stderr index c4f4c5f..d3a3ea7 100644 --- a/tests/ui/private.stderr +++ b/tests/ui/private.stderr @@ -1,5 +1,5 @@ error: functions tagged with `#[proc_macro_hack]` must be `pub` - --> $DIR/tests/ui/private.rs:4:1 + --> tests/ui/private.rs:4:1 | 4 | fn my_macro(input: TokenStream) -> TokenStream { | ^^ diff --git a/tests/ui/unexpected-arg.stderr b/tests/ui/unexpected-arg.stderr index 1243ead..f32f05a 100644 --- a/tests/ui/unexpected-arg.stderr +++ b/tests/ui/unexpected-arg.stderr @@ -1,5 +1,5 @@ error: unexpected argument to proc_macro_hack macro implementation; args are only accepted on the macro declaration (the `pub use`) - --> $DIR/tests/ui/unexpected-arg.rs:3:19 + --> tests/ui/unexpected-arg.rs:3:19 | 3 | #[proc_macro_hack(fake_call_site)] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/unexpected.stderr b/tests/ui/unexpected.stderr index afe2bd4..60761b5 100644 --- a/tests/ui/unexpected.stderr +++ b/tests/ui/unexpected.stderr @@ -1,5 +1,5 @@ error: unexpected input to #[proc_macro_hack] - --> $DIR/tests/ui/unexpected.rs:4:5 + --> tests/ui/unexpected.rs:4:5 | 4 | pub struct What; | ^^^^^^ diff --git a/tests/ui/unknown-arg.stderr b/tests/ui/unknown-arg.stderr index 2f685c0..772e667 100644 --- a/tests/ui/unknown-arg.stderr +++ b/tests/ui/unknown-arg.stderr @@ -1,5 +1,5 @@ error: expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`, `only_hack_old_rustc` - --> $DIR/tests/ui/unknown-arg.rs:3:35 + --> tests/ui/unknown-arg.rs:3:35 | 3 | #[proc_macro_hack(fake_call_site, support_nexted)] | ^^^^^^^^^^^^^^ From aee39c97871ca2d30cf08e3ed23fd2ad901c644a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 8 Oct 2021 02:51:26 -0400 Subject: [PATCH 77/93] Opt in to experimental trybuild diffs --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cfa8285..238c2db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ demo-hack-impl = { version = "0.0.5", path = "demo-hack-impl" } quote = "1.0" rustversion = "1.0" syn = "1.0.5" -trybuild = "1.0.49" +trybuild = { version = "1.0.49", features = ["diff"] } [workspace] members = ["demo-hack", "demo-hack-impl", "example", "nested"] From fdf50c88ab8e83f25e052ca3f489f0fe5ba0f419 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 17 Nov 2021 13:29:48 -0800 Subject: [PATCH 78/93] Install newest build of cargo-outdated The old version available by default in the GitHub Actions image fails with: thread 'main' panicked at 'package cache lock is not currently held, Cargo forgot to call `acquire_package_cache_lock` before we got to this stack frame', /usr/share/rust/.cargo/registry/src/github.com-1ecc6299db9ec823/cargo-0.57.0/src/cargo/util/config/mod.rs:1542:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 940bd74..1ac0f29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,4 +44,5 @@ jobs: if: github.event_name != 'pull_request' steps: - uses: actions/checkout@v2 + - uses: dtolnay/install@cargo-outdated - run: cargo outdated --exit-code 1 From b6d8b67578fa81660b54a2b724612ad499dff20b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 11 Dec 2021 18:44:13 -0800 Subject: [PATCH 79/93] Raise minimum rustc for test suite from 1.34 to 1.36 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ac0f29..7fefc4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, stable, 1.45.0, 1.42.0, 1.34.0, 1.31.0] + rust: [beta, stable, 1.45.0, 1.42.0, 1.36.0, 1.31.0] include: - rust: nightly - rust: nightly @@ -23,7 +23,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} - - if: matrix.rust == '1.31.0' || matrix.rust == '1.34.0' + - if: matrix.rust == '1.31.0' || matrix.rust == '1.36.0' run: echo 'extern crate proc_macro;' >> demo-hack-impl/src/lib.rs - run: cargo run --manifest-path example/Cargo.toml - if: matrix.rust != '1.31.0' From bcd41e2da47949ce44eaae4b7c166e749048907b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 31 Dec 2021 22:20:01 -0800 Subject: [PATCH 80/93] Detect warnings in CI --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fefc4d..6395f4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,9 @@ on: pull_request: schedule: [cron: "40 1 * * *"] +env: + RUSTFLAGS: '-Dwarnings' + jobs: test: name: ${{matrix.name || format('Rust {0}', matrix.rust)}} From 287c21f6830a2637723fe406c4992ddea1ce1673 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 25 Apr 2022 02:05:50 -0700 Subject: [PATCH 81/93] Update workflows to actions/checkout@v3 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6395f4b..62a78e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: name: Windows os: windows steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-latest if: github.event_name != 'pull_request' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@clippy - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic @@ -46,6 +46,6 @@ jobs: runs-on: ubuntu-latest if: github.event_name != 'pull_request' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/install@cargo-outdated - run: cargo outdated --exit-code 1 From 78f71b9f8eb422f35bbc62ed9495a96f947b9a32 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:09:24 -0800 Subject: [PATCH 82/93] Drop unneeded quoting from env variable in workflows yaml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62a78e3..41a2bac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: schedule: [cron: "40 1 * * *"] env: - RUSTFLAGS: '-Dwarnings' + RUSTFLAGS: -Dwarnings jobs: test: From 29071f0972ec0cca9a8553779d436475099e774b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:16:08 -0800 Subject: [PATCH 83/93] Raise minimum tested toolchain to rust 1.56 Required by the transitive dev-dependency on once_cell. error: failed to download `once_cell v1.16.0` Caused by: unable to get packages from source Caused by: failed to parse manifest at `github.com-1ecc6299db9ec823/once_cell-1.16.0/Cargo.toml` Caused by: feature `edition2021` is required The package requires the Cargo feature called `edition2021`, but that feature is not stabilized in this version of Cargo (1.55.0 (32da73ab1 2021-08-23)). Consider trying a newer version of Cargo (this may require the nightly release). See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#edition-2021 for more information about the status of this feature. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41a2bac..bc96e04 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, stable, 1.45.0, 1.42.0, 1.36.0, 1.31.0] + rust: [beta, stable, 1.56.0, 1.31.0] include: - rust: nightly - rust: nightly @@ -26,7 +26,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} - - if: matrix.rust == '1.31.0' || matrix.rust == '1.36.0' + - if: matrix.rust == '1.31.0' run: echo 'extern crate proc_macro;' >> demo-hack-impl/src/lib.rs - run: cargo run --manifest-path example/Cargo.toml - if: matrix.rust != '1.31.0' From 757605fe7a934ae3645076db5aa5b0abbf435e6f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:13:51 -0800 Subject: [PATCH 84/93] Fix warning about let_underscore_drop lint removal from clippy warning: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` --> src/lib.rs:150:5 | 150 | clippy::let_underscore_drop, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` | = note: `#[warn(renamed_and_removed_lints)]` on by default --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index e616498..7b8b43b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -147,7 +147,6 @@ #![recursion_limit = "512"] #![allow( clippy::doc_markdown, - clippy::let_underscore_drop, clippy::manual_strip, clippy::module_name_repetitions, clippy::needless_doctest_main, From 6961bdf52f4115c8e43f6e6c554034ce9f681216 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:10:13 -0800 Subject: [PATCH 85/93] Check all crates in workspace for outdated deps --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc96e04..5170819 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,4 +48,4 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/install@cargo-outdated - - run: cargo outdated --exit-code 1 + - run: cargo outdated --workspace --exit-code 1 From 2c1764cc2c1586a2225ba422cb99fcfab6d1e345 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:10:32 -0800 Subject: [PATCH 86/93] GitHub Workflows security hardening --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5170819..cba28d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,9 @@ on: pull_request: schedule: [cron: "40 1 * * *"] +permissions: + contents: read + env: RUSTFLAGS: -Dwarnings From 16443d68d7fb65986df205900d0093d544ba6373 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:10:50 -0800 Subject: [PATCH 87/93] Time out workflows after 45 minutes --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cba28d4..6031016 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: - rust: nightly name: Windows os: windows + timeout-minutes: 45 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master @@ -39,6 +40,7 @@ jobs: name: Clippy runs-on: ubuntu-latest if: github.event_name != 'pull_request' + timeout-minutes: 45 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@clippy @@ -48,6 +50,7 @@ jobs: name: Outdated runs-on: ubuntu-latest if: github.event_name != 'pull_request' + timeout-minutes: 45 steps: - uses: actions/checkout@v3 - uses: dtolnay/install@cargo-outdated From 4945af4050d29b0fb8d31e443da2be0db336273f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:11:09 -0800 Subject: [PATCH 88/93] Update build status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c734710..e6211ea 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Procedural macros in expression position [github](https://github.com/dtolnay/proc-macro-hack) [crates.io](https://crates.io/crates/proc-macro-hack) [docs.rs](https://docs.rs/proc-macro-hack) -[build status](https://github.com/dtolnay/proc-macro-hack/actions?query=branch%3Amaster) +[build status](https://github.com/dtolnay/proc-macro-hack/actions?query=branch%3Amaster)

Note: As of Rust 1.45 this crate is superseded by native support for From d6e23bcacdf25b52e77dee05d7c4473edb435ddc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:11:32 -0800 Subject: [PATCH 89/93] Use upstreamed docs.rs icon in docs.rs badge --- README.md | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e6211ea..b72b8ef 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Procedural macros in expression position [github](https://github.com/dtolnay/proc-macro-hack) [crates.io](https://crates.io/crates/proc-macro-hack) -[docs.rs](https://docs.rs/proc-macro-hack) +[docs.rs](https://docs.rs/proc-macro-hack) [build status](https://github.com/dtolnay/proc-macro-hack/actions?query=branch%3Amaster)

diff --git a/src/lib.rs b/src/lib.rs index 7b8b43b..9a5a8ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust -//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs //! //!
//! From 62448ecf55b71a6c6adc3e94f8ebd71e309d8a0f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:12:15 -0800 Subject: [PATCH 90/93] Sort package entries in Cargo.toml --- Cargo.toml | 8 ++++---- demo-hack-impl/Cargo.toml | 4 ++-- demo-hack/Cargo.toml | 4 ++-- example/Cargo.toml | 4 ++-- nested/Cargo.toml | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 238c2db..651dfa2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,13 +2,13 @@ name = "proc-macro-hack" version = "0.5.19" authors = ["David Tolnay "] +categories = ["development-tools::procedural-macro-helpers"] +description = "Procedural macros in expression position" edition = "2018" -rust-version = "1.31" license = "MIT OR Apache-2.0" -description = "Procedural macros in expression position" -repository = "https://github.com/dtolnay/proc-macro-hack" -categories = ["development-tools::procedural-macro-helpers"] readme = "README.md" +repository = "https://github.com/dtolnay/proc-macro-hack" +rust-version = "1.31" [lib] proc-macro = true diff --git a/demo-hack-impl/Cargo.toml b/demo-hack-impl/Cargo.toml index dc9d85e..54c2db9 100644 --- a/demo-hack-impl/Cargo.toml +++ b/demo-hack-impl/Cargo.toml @@ -2,10 +2,10 @@ name = "demo-hack-impl" version = "0.0.5" authors = ["David Tolnay "] -license = "MIT OR Apache-2.0" description = "Demo of proc-macro-hack" -repository = "https://github.com/dtolnay/proc-macro-hack" edition = "2018" +license = "MIT OR Apache-2.0" +repository = "https://github.com/dtolnay/proc-macro-hack" [lib] proc-macro = true diff --git a/demo-hack/Cargo.toml b/demo-hack/Cargo.toml index 238b96c..50f81c1 100644 --- a/demo-hack/Cargo.toml +++ b/demo-hack/Cargo.toml @@ -2,10 +2,10 @@ name = "demo-hack" version = "0.0.5" authors = ["David Tolnay "] -license = "MIT OR Apache-2.0" description = "Demo of proc-macro-hack" -repository = "https://github.com/dtolnay/proc-macro-hack" edition = "2018" +license = "MIT OR Apache-2.0" +repository = "https://github.com/dtolnay/proc-macro-hack" [dependencies] proc-macro-hack = { version = "0.5", path = ".." } diff --git a/example/Cargo.toml b/example/Cargo.toml index 4c69129..e9ad4f3 100644 --- a/example/Cargo.toml +++ b/example/Cargo.toml @@ -2,10 +2,10 @@ name = "example" version = "0.0.0" authors = ["David Tolnay "] -license = "MIT OR Apache-2.0" -repository = "https://github.com/dtolnay/proc-macro-hack" edition = "2018" +license = "MIT OR Apache-2.0" publish = false +repository = "https://github.com/dtolnay/proc-macro-hack" [dependencies] demo-hack = { path = "../demo-hack" } diff --git a/nested/Cargo.toml b/nested/Cargo.toml index aa306db..1dfa83b 100644 --- a/nested/Cargo.toml +++ b/nested/Cargo.toml @@ -2,8 +2,8 @@ name = "proc-macro-nested" version = "0.1.7" authors = ["David Tolnay "] -license = "MIT OR Apache-2.0" description = "Support for nested proc-macro-hack invocations" +license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/proc-macro-hack" [package.metadata.docs.rs] From 4c26b5be642f39bef8f9048898a59018f0915404 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:12:57 -0800 Subject: [PATCH 91/93] Remove default package.readme metadata from Cargo.toml Since cargo 1.46.0, README.md is recognized by default. --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 651dfa2..0e3d293 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ categories = ["development-tools::procedural-macro-helpers"] description = "Procedural macros in expression position" edition = "2018" license = "MIT OR Apache-2.0" -readme = "README.md" repository = "https://github.com/dtolnay/proc-macro-hack" rust-version = "1.31" From fe0889cb7a67adfe070fad449066034e7a662f56 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:21:28 -0800 Subject: [PATCH 92/93] Release 0.5.20 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0e3d293..7e8b88e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro-hack" -version = "0.5.19" +version = "0.5.20+deprecated" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers"] description = "Procedural macros in expression position" From 9a299b12ba7c21d0e2fe2b3fc7a24874c17896b9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Dec 2022 10:37:39 -0800 Subject: [PATCH 93/93] Add documentation metadata linking to docs.rs --- Cargo.toml | 1 + nested/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 7e8b88e..7dab558 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.5.20+deprecated" authors = ["David Tolnay "] categories = ["development-tools::procedural-macro-helpers"] description = "Procedural macros in expression position" +documentation = "https://docs.rs/proc-macro-hack" edition = "2018" license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/proc-macro-hack" diff --git a/nested/Cargo.toml b/nested/Cargo.toml index 1dfa83b..8ff7b3d 100644 --- a/nested/Cargo.toml +++ b/nested/Cargo.toml @@ -3,6 +3,7 @@ name = "proc-macro-nested" version = "0.1.7" authors = ["David Tolnay "] description = "Support for nested proc-macro-hack invocations" +documentation = "https://docs.rs/proc-macro-nested" license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/proc-macro-hack"