Add internal '__typed_stream' proc-macro.

This resolves syntax ambiguity issues with public typed-stream macros.
Prior to this commit, greedy single-token matching by macro-rules macros
would result in certain tokens at the beginning of the macro input, such
as 'for', inadvertently triggering a '$ty' matching case resulting in
incorrect expansion.
This commit is contained in:
Sergio Benitez 2021-06-01 11:43:51 -07:00
parent 009be32a8c
commit ed3cc13b84
8 changed files with 84 additions and 19 deletions

View File

@ -1,12 +1,13 @@
use devise::Result;
use syn::{Path, punctuated::Punctuated, parse::Parser, Token};
use syn::spanned::Spanned;
use proc_macro2::TokenStream;
mod uri;
mod uri_parsing;
mod test_guide;
mod export;
mod typed_stream;
use devise::Result;
use syn::{Path, punctuated::Punctuated, parse::Parser, Token};
use syn::spanned::Spanned;
use proc_macro2::TokenStream;
fn struct_maker_vec(
input: proc_macro::TokenStream,
@ -70,3 +71,8 @@ pub fn export_internal(input: proc_macro::TokenStream) -> TokenStream {
export::_macro(input)
.unwrap_or_else(|diag| diag.emit_as_item_tokens())
}
pub fn typed_stream(input: proc_macro::TokenStream) -> TokenStream {
typed_stream::_macro(input)
.unwrap_or_else(|diag| diag.emit_as_item_tokens())
}

View File

@ -0,0 +1,53 @@
use proc_macro2::TokenStream;
use syn::parse::{Parse, ParseStream, discouraged::Speculative};
enum Input {
Type(syn::Type),
Tokens(TokenStream)
}
struct Invocation {
ty_stream_ty: syn::Path,
stream_mac: syn::Path,
stream_trait: syn::Path,
input: Input,
}
impl Parse for Invocation {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let ty_stream_ty = input.parse()?;
input.parse::<syn::Token![,]>()?;
let stream_mac = input.parse()?;
input.parse::<syn::Token![,]>()?;
let stream_trait = input.parse()?;
input.parse::<syn::Token![,]>()?;
let fork = input.fork();
let input = match fork.parse() {
Ok(ty) => {
input.advance_to(&fork);
Input::Type(ty)
}
Err(_) => Input::Tokens(input.parse()?)
};
Ok(Invocation { ty_stream_ty, stream_mac, stream_trait, input })
}
}
/// This macro exists because we want to disambiguate between input of a type
/// and input of an expression that looks like a type. `macro_rules` matches
/// eagerly on a single token, so something like `foo!(for x in 0..10 {})` will
/// match a `($ty)` branch as will anything that starts with a path.
pub fn _macro(input: proc_macro::TokenStream) -> devise::Result<TokenStream> {
let i: Invocation = syn::parse(input)?;
let (s_ty, mac, s_trait) = (i.ty_stream_ty, i.stream_mac, i.stream_trait);
let tokens = match i.input {
Input::Tokens(tt) => quote!(#s_ty::from(#mac!(#tt))),
Input::Type(ty) => quote!(#s_ty<impl #s_trait<Item = #ty>>),
};
Ok(tokens)
}

View File

@ -1345,23 +1345,30 @@ pub fn uri(input: TokenStream) -> TokenStream {
emit!(bang::uri_macro(input))
}
#[doc(hidden)]
#[proc_macro]
/// Internal macro: `rocket_internal_uri!`.
#[proc_macro]
#[doc(hidden)]
pub fn rocket_internal_uri(input: TokenStream) -> TokenStream {
emit!(bang::uri_internal_macro(input))
}
#[doc(hidden)]
/// Internal macro: `__typed_stream!`.
#[proc_macro]
#[doc(hidden)]
pub fn __typed_stream(input: TokenStream) -> TokenStream {
emit!(bang::typed_stream(input))
}
/// Private Rocket internal macro: `internal_guide_tests!`.
#[proc_macro]
#[doc(hidden)]
pub fn internal_guide_tests(input: TokenStream) -> TokenStream {
emit!(bang::guide_tests_internal(input))
}
#[doc(hidden)]
#[proc_macro]
/// Private Rocket internal macro: `export!`.
#[proc_macro]
#[doc(hidden)]
pub fn export(input: TokenStream) -> TokenStream {
emit!(bang::export_internal(input))
}

View File

@ -7,7 +7,7 @@
use rocket::{Request, Rocket, Build};
use rocket::local::blocking::Client;
use rocket::http::{Status, ContentType};
use rocket::http::Status;
#[catch(404)] fn not_found_0() -> &'static str { "404-0" }
#[catch(404)] fn not_found_1(_: &Request) -> &'static str { "404-1" }

View File

@ -78,7 +78,6 @@ crate::export! {
/// See [`struct@ByteStream`] and the [module level
/// docs](crate::response::stream#typed-streams) for usage details.
macro_rules! ByteStream {
($T:ty) => ($crate::_typed_stream!(ByteStream, $T));
($($s:tt)*) => ($crate::_typed_stream!(ByteStream, $($s)*));
}
}

View File

@ -223,10 +223,12 @@ crate::export! {
#[doc(hidden)]
#[macro_export]
macro_rules! _typed_stream {
($S:ident, $T:ty) => (
$crate::response::stream::$S<impl $crate::futures::stream::Stream<Item = $T>>
);
($S:ident, $($t:tt)*) => (
$crate::response::stream::$S::from($crate::response::stream::stream!($($t)*))
);
$crate::__typed_stream! {
$crate::response::stream::$S,
$crate::response::stream::stream,
$crate::futures::stream::Stream,
$($t)*
}
)
}

View File

@ -199,7 +199,6 @@ crate::export! {
/// See [`struct@ReaderStream`] and the [module level
/// docs](crate::response::stream#typed-streams) for usage details.
macro_rules! ReaderStream {
($T:ty) => ($crate::_typed_stream!(ReaderStream, $T));
($($s:tt)*) => ($crate::_typed_stream!(ReaderStream, $($s)*));
}
}

View File

@ -88,7 +88,6 @@ crate::export! {
/// See [`struct@TextStream`] and the [module level
/// docs](crate::response::stream#typed-streams) for usage details.
macro_rules! TextStream {
($T:ty) => ($crate::_typed_stream!(TextStream, $T));
($($s:tt)*) => ($crate::_typed_stream!(TextStream, $($s)*));
}
}