diff --git a/crates/macro/src/html_tree/html_component.rs b/crates/macro/src/html_tree/html_component.rs
index f2d833e58e1..56b4820b33c 100644
--- a/crates/macro/src/html_tree/html_component.rs
+++ b/crates/macro/src/html_tree/html_component.rs
@@ -340,17 +340,22 @@ impl ToTokens for HtmlComponentClose {
}
}
-enum PropType {
- List,
- With,
-}
-
enum Props {
List(Box),
With(Box),
None,
}
+struct ListProps {
+ props: Vec,
+ node_ref: Option,
+}
+
+struct WithProps {
+ props: Ident,
+ node_ref: Option,
+}
+
impl Props {
fn node_ref(&self) -> Option<&Expr> {
match self {
@@ -359,102 +364,101 @@ impl Props {
Props::None => None,
}
}
-}
-impl PeekValue for Props {
- fn peek(cursor: Cursor) -> Option {
- let (ident, _) = cursor.ident()?;
- let prop_type = if ident == "with" {
- PropType::With
- } else {
- PropType::List
- };
-
- Some(prop_type)
+ fn collision_message() -> &'static str {
+ "Using special syntax `with props` along with named prop is not allowed. This rule does not apply to special `ref` prop"
}
}
impl Parse for Props {
fn parse(input: ParseStream) -> ParseResult {
- match Props::peek(input.cursor()) {
- Some(PropType::List) => input.parse().map(|l| Props::List(Box::new(l))),
- Some(PropType::With) => input.parse().map(|w| Props::With(Box::new(w))),
- None => Ok(Props::None),
- }
- }
-}
-
-struct ListProps {
- props: Vec,
- node_ref: Option,
-}
+ let mut props = Props::None;
+ let mut node_ref: Option = None;
+
+ while let Some((token, _)) = input.cursor().ident() {
+ if token == "with" {
+ match props {
+ Props::None => Ok(()),
+ Props::With(_) => Err(input.error("too many `with` tokens used")),
+ Props::List(_) => {
+ Err(syn::Error::new_spanned(&token, Props::collision_message()))
+ }
+ }?;
+
+ input.parse::()?;
+ props = Props::With(Box::new(WithProps {
+ props: input.parse::()?,
+ node_ref: None,
+ }));
+
+ // Handle optional comma
+ let _ = input.parse::();
+ continue;
+ }
-impl Parse for ListProps {
- fn parse(input: ParseStream) -> ParseResult {
- let mut props: Vec = Vec::new();
- while HtmlProp::peek(input.cursor()).is_some() {
- props.push(input.parse::()?);
- }
+ if (HtmlProp::peek(input.cursor())).is_none() {
+ break;
+ }
- let ref_position = props.iter().position(|p| p.label.to_string() == "ref");
- let node_ref = ref_position.map(|i| props.remove(i).value);
- for prop in &props {
+ let prop = input.parse::()?;
if prop.label.to_string() == "ref" {
- return Err(syn::Error::new_spanned(&prop.label, "too many refs set"));
+ match node_ref {
+ None => Ok(()),
+ Some(_) => Err(syn::Error::new_spanned(&prop.label, "too many refs set")),
+ }?;
+
+ node_ref = Some(prop.value);
+ continue;
}
+
if prop.label.to_string() == "type" {
return Err(syn::Error::new_spanned(&prop.label, "expected identifier"));
}
+
if !prop.label.extended.is_empty() {
return Err(syn::Error::new_spanned(&prop.label, "expected identifier"));
}
- }
-
- // alphabetize
- props.sort_by(|a, b| {
- if a.label == b.label {
- Ordering::Equal
- } else if a.label.to_string() == "children" {
- Ordering::Greater
- } else if b.label.to_string() == "children" {
- Ordering::Less
- } else {
- a.label
- .to_string()
- .partial_cmp(&b.label.to_string())
- .unwrap()
- }
- });
-
- Ok(ListProps { props, node_ref })
- }
-}
-
-struct WithProps {
- props: Ident,
- node_ref: Option,
-}
-impl Parse for WithProps {
- fn parse(input: ParseStream) -> ParseResult {
- let with = input.parse::()?;
- if with != "with" {
- return Err(input.error("expected to find `with` token"));
+ match props {
+ ref mut props @ Props::None => {
+ *props = Props::List(Box::new(ListProps {
+ props: vec![prop],
+ node_ref: None,
+ }));
+ }
+ Props::With(_) => {
+ return Err(syn::Error::new_spanned(&token, Props::collision_message()))
+ }
+ Props::List(ref mut list) => {
+ list.props.push(prop);
+ }
+ };
}
- let props = input.parse::()?;
- let _ = input.parse::();
- // Check for the ref tag after `with`
- let mut node_ref = None;
- if let Some(ident) = input.cursor().ident() {
- let prop = input.parse::()?;
- if ident.0 == "ref" {
- node_ref = Some(prop.value);
- } else {
- return Err(syn::Error::new_spanned(&prop.label, "unexpected token"));
+ match props {
+ Props::None => {}
+ Props::With(ref mut p) => p.node_ref = node_ref,
+ Props::List(ref mut p) => {
+ p.node_ref = node_ref;
+
+ // alphabetize
+ p.props.sort_by(|a, b| {
+ if a.label == b.label {
+ Ordering::Equal
+ } else if a.label.to_string() == "children" {
+ Ordering::Greater
+ } else if b.label.to_string() == "children" {
+ Ordering::Less
+ } else {
+ a.label
+ .to_string()
+ .partial_cmp(&b.label.to_string())
+ .unwrap()
+ }
+ });
}
- }
+ };
- Ok(WithProps { props, node_ref })
+ Ok(props)
}
}
diff --git a/crates/macro/tests/macro/html-component-fail.rs b/crates/macro/tests/macro/html-component-fail.rs
index 6a9013d7031..0a42e145740 100644
--- a/crates/macro/tests/macro/html-component-fail.rs
+++ b/crates/macro/tests/macro/html-component-fail.rs
@@ -73,9 +73,16 @@ fn compile_fail() {
html! { };
html! { };
html! { };
- html! { };
+ html! { };
+ html! { };
+ html! { };
+ html! { };
+ html! { };
+ html! { };
html! { };
html! { };
+ html! { };
+ html! { };
html! { };
html! { };
html! { };
diff --git a/crates/macro/tests/macro/html-component-fail.stderr b/crates/macro/tests/macro/html-component-fail.stderr
index 3f792855b44..82de6abf39d 100644
--- a/crates/macro/tests/macro/html-component-fail.stderr
+++ b/crates/macro/tests/macro/html-component-fail.stderr
@@ -30,107 +30,149 @@ error: this open tag has no corresponding close tag
74 | html! { };
| ^^^^^^^^^^^^^^^^^^^
-error: unexpected token
+error: too many refs set
--> $DIR/html-component-fail.rs:75:38
|
75 | html! { };
| ^^^
-error: unexpected token
- --> $DIR/html-component-fail.rs:76:27
+error: too many refs set
+ --> $DIR/html-component-fail.rs:76:38
|
-76 | html! { };
- | ^^^^
+76 | html! { };
+ | ^^^
-error: unexpected token
+error: Using special syntax `with props` along with named prop is not allowed. This rule does not apply to special `ref` prop
+ --> $DIR/html-component-fail.rs:77:38
+ |
+77 | html! { };
+ | ^^^^^
+
+error: Using special syntax `with props` along with named prop is not allowed. This rule does not apply to special `ref` prop
--> $DIR/html-component-fail.rs:78:31
|
-78 | html! { };
+78 | html! { };
+ | ^^^^^
+
+error: Using special syntax `with props` along with named prop is not allowed. This rule does not apply to special `ref` prop
+ --> $DIR/html-component-fail.rs:79:28
+ |
+79 | html! { };
+ | ^^^^
+
+error: Using special syntax `with props` along with named prop is not allowed. This rule does not apply to special `ref` prop
+ --> $DIR/html-component-fail.rs:80:35
+ |
+80 | html! { };
+ | ^^^^
+
+error: too many refs set
+ --> $DIR/html-component-fail.rs:81:27
+ |
+81 | html! { };
+ | ^^^
+
+error: unexpected token
+ --> $DIR/html-component-fail.rs:83:31
+ |
+83 | html! { };
| ^^
+error: Using special syntax `with props` along with named prop is not allowed. This rule does not apply to special `ref` prop
+ --> $DIR/html-component-fail.rs:84:28
+ |
+84 | html! { };
+ | ^^^^
+
+error: Using special syntax `with props` along with named prop is not allowed. This rule does not apply to special `ref` prop
+ --> $DIR/html-component-fail.rs:85:31
+ |
+85 | html! { };
+ | ^^^^^
+
error: expected identifier
- --> $DIR/html-component-fail.rs:79:20
+ --> $DIR/html-component-fail.rs:86:20
|
-79 | html! { };
+86 | html! { };
| ^^^^
error: expected identifier
- --> $DIR/html-component-fail.rs:80:20
+ --> $DIR/html-component-fail.rs:87:20
|
-80 | html! { };
+87 | html! { };
| ^^^^^^^^^^^^^^^^^
error: unexpected end of input, expected expression
- --> $DIR/html-component-fail.rs:82:5
+ --> $DIR/html-component-fail.rs:89:5
|
-82 | html! { };
+89 | html! { };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: too many refs set
- --> $DIR/html-component-fail.rs:87:33
+ --> $DIR/html-component-fail.rs:94:33
|
-87 | html! { };
+94 | html! { };
| ^^^
error: this close tag has no corresponding open tag
- --> $DIR/html-component-fail.rs:90:13
+ --> $DIR/html-component-fail.rs:97:13
|
-90 | html! { };
+97 | html! { };
| ^^^^^^^^
error: this open tag has no corresponding close tag
- --> $DIR/html-component-fail.rs:91:13
+ --> $DIR/html-component-fail.rs:98:13
|
-91 | html! { };
+98 | html! { };
| ^^^^^^^
error: only one root html element allowed
- --> $DIR/html-component-fail.rs:92:28
+ --> $DIR/html-component-fail.rs:99:28
|
-92 | html! { };
+99 | html! { };
| ^^^^^^^^^^^^^^^
error: this close tag has no corresponding open tag
- --> $DIR/html-component-fail.rs:101:30
+ --> $DIR/html-component-fail.rs:108:30
|
-101 | html! { > };
+108 | html! { > };
| ^^^^^^^^^^
error: this close tag has no corresponding open tag
- --> $DIR/html-component-fail.rs:102:30
+ --> $DIR/html-component-fail.rs:109:30
|
-102 | html! { >>> };
+109 | html! { >>> };
| ^^^^^^^^^^^^^^^^^^^^^^^
error[E0425]: cannot find value `blah` in this scope
- --> $DIR/html-component-fail.rs:77:25
+ --> $DIR/html-component-fail.rs:82:25
|
-77 | html! { };
+82 | html! { };
| ^^^^ not found in this scope
error[E0609]: no field `unknown` on type `ChildProperties`
- --> $DIR/html-component-fail.rs:81:20
+ --> $DIR/html-component-fail.rs:88:20
|
-81 | html! { };
+88 | html! { };
| ^^^^^^^ unknown field
|
= note: available fields are: `string`, `int`
error[E0599]: no method named `unknown` found for type `ChildPropertiesBuilder` in the current scope
- --> $DIR/html-component-fail.rs:81:20
+ --> $DIR/html-component-fail.rs:88:20
|
6 | #[derive(Clone, Properties, PartialEq)]
| ---------- method `unknown` not found for this
...
-81 | html! { };
+88 | html! { };
| ^^^^^^^ method not found in `ChildPropertiesBuilder`
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VComp: yew::virtual_dom::Transformer<(), std::string::String>` is not satisfied
- --> $DIR/html-component-fail.rs:83:33
+ --> $DIR/html-component-fail.rs:90:33
|
-83 | html! { };
+90 | html! { };
| ^^ the trait `yew::virtual_dom::Transformer<(), std::string::String>` is not implemented for `yew::virtual_dom::vcomp::VComp`
|
= help: the following implementations were found:
@@ -142,9 +184,9 @@ error[E0277]: the trait bound `yew::virtual_dom::vcomp::VComp: yew::virtual_dom:
= note: required by `yew::virtual_dom::Transformer::transform`
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VComp: yew::virtual_dom::Transformer<{integer}, std::string::String>` is not satisfied
- --> $DIR/html-component-fail.rs:84:33
+ --> $DIR/html-component-fail.rs:91:33
|
-84 | html! { };
+91 | html! { };
| ^ the trait `yew::virtual_dom::Transformer<{integer}, std::string::String>` is not implemented for `yew::virtual_dom::vcomp::VComp`
|
= help: the following implementations were found:
@@ -156,9 +198,9 @@ error[E0277]: the trait bound `yew::virtual_dom::vcomp::VComp: yew::virtual_dom:
= note: required by `yew::virtual_dom::Transformer::transform`
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VComp: yew::virtual_dom::Transformer<{integer}, std::string::String>` is not satisfied
- --> $DIR/html-component-fail.rs:85:33
+ --> $DIR/html-component-fail.rs:92:33
|
-85 | html! { };
+92 | html! { };
| ^^^ the trait `yew::virtual_dom::Transformer<{integer}, std::string::String>` is not implemented for `yew::virtual_dom::vcomp::VComp`
|
= help: the following implementations were found:
@@ -170,15 +212,15 @@ error[E0277]: the trait bound `yew::virtual_dom::vcomp::VComp: yew::virtual_dom:
= note: required by `yew::virtual_dom::Transformer::transform`
error[E0308]: mismatched types
- --> $DIR/html-component-fail.rs:86:30
+ --> $DIR/html-component-fail.rs:93:30
|
-86 | html! { };
+93 | html! { };
| ^^ expected struct `yew::html::NodeRef`, found `()`
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VComp: yew::virtual_dom::Transformer` is not satisfied
- --> $DIR/html-component-fail.rs:88:24
+ --> $DIR/html-component-fail.rs:95:24
|
-88 | html! { };
+95 | html! { };
| ^^^^ the trait `yew::virtual_dom::Transformer` is not implemented for `yew::virtual_dom::vcomp::VComp`
|
= help: the following implementations were found:
@@ -190,12 +232,12 @@ error[E0277]: the trait bound `yew::virtual_dom::vcomp::VComp: yew::virtual_dom:
= note: required by `yew::virtual_dom::Transformer::transform`
error[E0599]: no method named `string` found for type `ChildPropertiesBuilder` in the current scope
- --> $DIR/html-component-fail.rs:89:20
+ --> $DIR/html-component-fail.rs:96:20
|
6 | #[derive(Clone, Properties, PartialEq)]
| ---------- method `string` not found for this
...
-89 | html! { };
+96 | html! { };
| ^^^^^^ method not found in `ChildPropertiesBuilder`
|
= help: items from traits can only be used if the trait is implemented and in scope
@@ -203,70 +245,70 @@ error[E0599]: no method named `string` found for type `ChildPropertiesBuilder` in the current scope
- --> $DIR/html-component-fail.rs:93:5
- |
-6 | #[derive(Clone, Properties, PartialEq)]
- | ---------- method `children` not found for this
+ --> $DIR/html-component-fail.rs:100:5
+ |
+6 | #[derive(Clone, Properties, PartialEq)]
+ | ---------- method `children` not found for this
...
-93 | html! { { "Not allowed" } };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildPropertiesBuilder`
- |
- = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+100 | html! { { "Not allowed" } };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildPropertiesBuilder`
+ |
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0599]: no method named `build` found for type `ChildContainerPropertiesBuilder` in the current scope
- --> $DIR/html-component-fail.rs:95:5
- |
-29 | #[derive(Clone, Properties)]
- | ---------- method `build` not found for this
+ --> $DIR/html-component-fail.rs:102:5
+ |
+29 | #[derive(Clone, Properties)]
+ | ---------- method `build` not found for this
...
-95 | html! { };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildContainerPropertiesBuilder`
- |
- = help: items from traits can only be used if the trait is implemented and in scope
- = note: the following trait defines an item `build`, perhaps you need to implement it:
- candidate #1: `proc_macro::bridge::server::TokenStreamBuilder`
- = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+102 | html! { };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildContainerPropertiesBuilder`
+ |
+ = help: items from traits can only be used if the trait is implemented and in scope
+ = note: the following trait defines an item `build`, perhaps you need to implement it:
+ candidate #1: `proc_macro::bridge::server::TokenStreamBuilder`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0599]: no method named `build` found for type `ChildContainerPropertiesBuilder` in the current scope
- --> $DIR/html-component-fail.rs:96:5
- |
-29 | #[derive(Clone, Properties)]
- | ---------- method `build` not found for this
+ --> $DIR/html-component-fail.rs:103:5
+ |
+29 | #[derive(Clone, Properties)]
+ | ---------- method `build` not found for this
...
-96 | html! { };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildContainerPropertiesBuilder`
- |
- = help: items from traits can only be used if the trait is implemented and in scope
- = note: the following trait defines an item `build`, perhaps you need to implement it:
- candidate #1: `proc_macro::bridge::server::TokenStreamBuilder`
- = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+103 | html! { };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `ChildContainerPropertiesBuilder`
+ |
+ = help: items from traits can only be used if the trait is implemented and in scope
+ = note: the following trait defines an item `build`, perhaps you need to implement it:
+ candidate #1: `proc_macro::bridge::server::TokenStreamBuilder`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild: std::convert::From<&str>` is not satisfied
- --> $DIR/html-component-fail.rs:97:5
- |
-97 | html! { { "Not allowed" } };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<&str>` is not implemented for `yew::virtual_dom::vcomp::VChild`
- |
- = note: required because of the requirements on the impl of `std::convert::Into>` for `&str`
- = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `yew::utils::NodeSeq<&str, yew::virtual_dom::vcomp::VChild>`
- = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+ --> $DIR/html-component-fail.rs:104:5
+ |
+104 | html! { { "Not allowed" } };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<&str>` is not implemented for `yew::virtual_dom::vcomp::VChild`
+ |
+ = note: required because of the requirements on the impl of `std::convert::Into>` for `&str`
+ = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `yew::utils::NodeSeq<&str, yew::virtual_dom::vcomp::VChild>`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild: std::convert::From` is not satisfied
- --> $DIR/html-component-fail.rs:98:5
- |
-98 | html! { <>> };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From` is not implemented for `yew::virtual_dom::vcomp::VChild`
- |
- = note: required because of the requirements on the impl of `std::convert::Into>` for `yew::virtual_dom::vnode::VNode`
- = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `yew::utils::NodeSeq>`
- = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+ --> $DIR/html-component-fail.rs:105:5
+ |
+105 | html! { <>> };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From` is not implemented for `yew::virtual_dom::vcomp::VChild`
+ |
+ = note: required because of the requirements on the impl of `std::convert::Into>` for `yew::virtual_dom::vnode::VNode`
+ = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `yew::utils::NodeSeq>`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild: std::convert::From` is not satisfied
- --> $DIR/html-component-fail.rs:99:5
- |
-99 | html! { };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From` is not implemented for `yew::virtual_dom::vcomp::VChild`
- |
- = note: required because of the requirements on the impl of `std::convert::Into>` for `yew::virtual_dom::vnode::VNode`
- = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `yew::utils::NodeSeq>`
- = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+ --> $DIR/html-component-fail.rs:106:5
+ |
+106 | html! { };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From` is not implemented for `yew::virtual_dom::vcomp::VChild`
+ |
+ = note: required because of the requirements on the impl of `std::convert::Into>` for `yew::virtual_dom::vnode::VNode`
+ = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `yew::utils::NodeSeq>`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
diff --git a/crates/macro/tests/macro/html-component-pass.rs b/crates/macro/tests/macro/html-component-pass.rs
index 8f1d823960c..cf70b1e3825 100644
--- a/crates/macro/tests/macro/html-component-pass.rs
+++ b/crates/macro/tests/macro/html-component-pass.rs
@@ -181,12 +181,15 @@ fn compile_pass() {
let props = ::Properties::default();
let props2 = ::Properties::default();
+ let props3 = ::Properties::default();
+ let props4 = ::Properties::default();
+ let node_ref = NodeRef::default();
html! {
<>
-
- // backwards compat
-
+ // backwards compat
+
+
>
};