Skip to content

Commit

Permalink
Fix issues with tuples in closing tag
Browse files Browse the repository at this point in the history
  • Loading branch information
hamza1311 committed Sep 25, 2022
1 parent e6e846c commit a9038da
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 158 deletions.
161 changes: 12 additions & 149 deletions packages/yew-macro/src/html_tree/html_component.rs
@@ -1,18 +1,11 @@
use boolinator::Boolinator;
use proc_macro2::Span;
use quote::{quote, quote_spanned, ToTokens};
use syn::buffer::Cursor;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{
AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, PathSegment, Token, Type,
TypePath,
};
use syn::{Token, Type};

use super::{HtmlChildrenTree, TagTokens};
use crate::props::ComponentProps;
use crate::PeekValue;

pub struct HtmlComponent {
ty: Type,
Expand All @@ -21,17 +14,16 @@ pub struct HtmlComponent {
close: Option<HtmlComponentClose>,
}

impl PeekValue<()> for HtmlComponent {
fn peek(cursor: Cursor) -> Option<()> {
HtmlComponentOpen::peek(cursor)
.or_else(|| HtmlComponentClose::peek(cursor))
.map(|_| ())
}
}

impl Parse for HtmlComponent {
fn parse(input: ParseStream) -> syn::Result<Self> {
if HtmlComponentClose::peek(input.cursor()).is_some() {
// check if the next tokens are </
let trying_to_close = || {
let lt = input.peek(Token![<]);
let div = input.peek2(Token![/]);
lt && div
};

if trying_to_close() {
return match input.parse::<HtmlComponentClose>() {
Ok(close) => Err(syn::Error::new_spanned(
close.to_spanned(),
Expand Down Expand Up @@ -60,12 +52,10 @@ impl Parse for HtmlComponent {
"this opening tag has no corresponding closing tag",
));
}
if let Some(ty) = HtmlComponentClose::peek(input.cursor()) {
if open.ty == ty {
break;
}
}

if trying_to_close() {
break;
}
children.parse_child(input)?;
}

Expand Down Expand Up @@ -127,108 +117,6 @@ impl ToTokens for HtmlComponent {
}
}

impl HtmlComponent {
fn double_colon(mut cursor: Cursor) -> Option<Cursor> {
for _ in 0..2 {
let (punct, c) = cursor.punct()?;
(punct.as_char() == ':').as_option()?;
cursor = c;
}

Some(cursor)
}

/// Refer to the [`syn::parse::Parse`] implementation for [`AngleBracketedGenericArguments`].
fn path_arguments(mut cursor: Cursor) -> Option<(PathArguments, Cursor)> {
let (punct, c) = cursor.punct()?;
cursor = c;
(punct.as_char() == '<').as_option()?;

let mut args = Punctuated::new();

loop {
let punct = cursor.punct();
if let Some((punct, c)) = punct {
if punct.as_char() == '>' {
cursor = c;
break;
}
}

let (ty, c) = Self::peek_type(cursor);
cursor = c;

args.push_value(GenericArgument::Type(ty));

let punct = cursor.punct();
if let Some((punct, c)) = punct {
cursor = c;
if punct.as_char() == '>' {
break;
} else if punct.as_char() == ',' {
args.push_punct(Token![,](Span::mixed_site()))
}
}
}

Some((
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
colon2_token: None,
lt_token: Token![<](Span::mixed_site()),
args,
gt_token: Token![>](Span::mixed_site()),
}),
cursor,
))
}

fn peek_type(mut cursor: Cursor) -> (Type, Cursor) {
let mut colons_optional = true;
let mut leading_colon = None;
let mut segments = Punctuated::new();

loop {
let mut post_colons_cursor = cursor;
if let Some(c) = Self::double_colon(post_colons_cursor) {
if colons_optional {
leading_colon = Some(Token![::](Span::mixed_site()));
}
post_colons_cursor = c;
} else if !colons_optional {
break;
}

if let Some((ident, c)) = post_colons_cursor.ident() {
cursor = c;
let arguments = if let Some((args, c)) = Self::path_arguments(cursor) {
cursor = c;
args
} else {
PathArguments::None
};

segments.push(PathSegment { ident, arguments });
} else {
break;
}

// only first `::` is optional
colons_optional = false;
}

(
Type::Path(TypePath {
qself: None,
path: Path {
leading_colon,
segments,
},
}),
cursor,
)
}
}

struct HtmlComponentOpen {
tag: TagTokens,
ty: Type,
Expand All @@ -244,15 +132,6 @@ impl HtmlComponentOpen {
}
}

impl PeekValue<Type> for HtmlComponentOpen {
fn peek(cursor: Cursor) -> Option<Type> {
let (punct, cursor) = cursor.punct()?;
(punct.as_char() == '<').as_option()?;
let (typ, _) = HtmlComponent::peek_type(cursor);
Some(typ)
}
}

impl Parse for HtmlComponentOpen {
fn parse(input: ParseStream) -> syn::Result<Self> {
TagTokens::parse_start_content(input, |input, tag| {
Expand Down Expand Up @@ -282,22 +161,6 @@ impl HtmlComponentClose {
}
}

impl PeekValue<Type> for HtmlComponentClose {
fn peek(cursor: Cursor) -> Option<Type> {
let (punct, cursor) = cursor.punct()?;
(punct.as_char() == '<').as_option()?;

let (punct, cursor) = cursor.punct()?;
(punct.as_char() == '/').as_option()?;

let (typ, cursor) = HtmlComponent::peek_type(cursor);

let (punct, _) = cursor.punct()?;
(punct.as_char() == '>').as_option()?;

Some(typ)
}
}
impl Parse for HtmlComponentClose {
fn parse(input: ParseStream) -> syn::Result<Self> {
TagTokens::parse_end_content(input, |input, tag| {
Expand Down
22 changes: 13 additions & 9 deletions packages/yew-macro/tests/html_macro/generic-component-fail.stderr
@@ -1,17 +1,21 @@
error: this opening tag has no corresponding closing tag
--> $DIR/generic-component-fail.rs:43:13
--> tests/html_macro/generic-component-fail.rs:43:13
|
43 | html! { <Generic<String>> };
| ^^^^^^^^^^^^^^^^^

error: this closing tag has no corresponding opening tag
--> $DIR/generic-component-fail.rs:44:30
error[E0107]: missing generics for struct `Generic`
--> tests/html_macro/generic-component-fail.rs:44:32
|
44 | html! { <Generic<String>></Generic> };
| ^^^^^^^^^^

error: this closing tag has no corresponding opening tag
--> $DIR/generic-component-fail.rs:45:30
| ^^^^^^^ expected 1 generic argument
|
note: struct defined here, with 1 generic parameter: `T`
--> tests/html_macro/generic-component-fail.rs:4:12
|
4 | pub struct Generic<T> {
| ^^^^^^^ -
help: add missing generic argument
|
45 | html! { <Generic<String>></Generic<Vec<String>>> };
| ^^^^^^^^^^^^^^^^^^^^^^^
44 | html! { <Generic<String>></Generic<T>> };
| ~~~~~~~~~~
Expand Up @@ -78,6 +78,7 @@ where
fn compile_pass() {
::yew::html! { <Generic<::std::string::String> /> };
::yew::html! { <Generic<(u8, bool)> /> };
::yew::html! { <Generic<(u8, bool)> ></Generic<(u8, bool)>> };
::yew::html! { <Generic<::std::string::String> ></Generic<::std::string::String>> };

::yew::html! { <Generic<::std::vec::Vec<::std::string::String>> /> };
Expand Down

0 comments on commit a9038da

Please sign in to comment.