-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
html!: fix mixed self-closing and non-sc tags #523
Conversation
I checked if the problem is actually allowed by the HTML specification, and it happens that it's not: https://stackoverflow.com/a/3558200/2209243 |
Hey @totorigolo I think this is actually worth supporting. JSX supports self-closing tags so I think developers will be accustomed to this short-hand even if it's not valid HTML. See here: https://reactjs.org/docs/jsx-in-depth.html |
59e90a9
to
57ffe3e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks awesome! I think your approach looks good, could you add one more test and fix the merge conflicts? Thanks!
crates/shared/tests/vtag_test.rs
Outdated
|
||
let a: VNode<Comp> = html! { | ||
<div> | ||
<div data-val={ 2 / 1 }/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add a test for an expression with a >
? Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works:
let a: VNode<Comp> = html! { <div> <div data-val={ 2 > 1 }/> </div> };
let b: VNode<Comp> = html! { <div> <div data-val={ true }></div> </div> };
assert_eq!(a, b);
But this doesn't:
let a: VNode<CompInt> = html! { <div> <div onblur=|_| 2 > 1/> </div> };
let b: VNode<CompInt> = html! { <div> <div onblur=|_| false></div> </div> };
assert_eq!(a, b); // NB: assert_eq! doesn't (cannot) compare the closures
It wasn't working before this PR, is now breaking with a less nice message. This should however happen very rarely, and the easy workaround is to add brackets around 2 > 1
. React's JSX doesn't have this problem AFAIK because it enforces putting brackets around expressions. I think that I'll take the pragmatic approach here and try to add a meaningful error message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The aforementioned failing test on master
:
diff --git a/tests/vtag_test.rs b/tests/vtag_test.rs
index 5ca5d6d..312888c 100644
--- a/tests/vtag_test.rs
+++ b/tests/vtag_test.rs
@@ -28,6 +28,27 @@ impl Renderable<Comp> for Comp {
}
}
+struct CompBool;
+
+impl Component for CompBool {
+ type Message = bool;
+ type Properties = ();
+
+ fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
+ CompBool
+ }
+
+ fn update(&mut self, _: Self::Message) -> ShouldRender {
+ unimplemented!();
+ }
+}
+
+impl Renderable<CompBool> for CompBool {
+ fn view(&self) -> Html<Self> {
+ unimplemented!();
+ }
+}
+
#[test]
fn it_compares_tags() {
let a: VNode<Comp> = html! {
@@ -258,3 +279,11 @@ fn it_allows_aria_attributes() {
panic!("vtag expected");
}
}
+
+#[test]
+fn this_fails() {
+ let a: VNode<CompBool> = html! { <div> <a onblur=|_| 2 > 1 /> </div> };
+ let b: VNode<CompBool> = html! { <div> <a onblur=|_| false></div> </div> };
+ assert_eq!(a, b);
+}
+
error: this open tag has no corresponding close tag
--> tests/vtag_test.rs:285:44
|
285 | let a: VNode<CompBool> = html! { <div> <a onblur=|_| 2 > 1 /> </div> };
| ^^^^^^^^^^^^^^^^^
error: this open tag has no corresponding close tag
--> tests/vtag_test.rs:286:44
|
286 | let b: VNode<CompBool> = html! { <div> <a onblur=|_| false></div> </div> };
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
error: Could not compile `yew`.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a feeling that was broken in the current macro implementation ;) thanks for adding tests and a better error message!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see three choices in order to provide better error messages:
- wait for
proc-macro
diagnostics, which are currently nightly-only, and just useeprintln!
in the meantime (this error should occur quite rarely). - mess around the actual code in order to return a result from verify_end.
- use global variables.
I implemented the first one for now. What do you think?
@jstarry How do you run failing tests like |
892856d
to
dd54b18
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great, just a few small changes needed
loop { | ||
if let Some(next_close_ident) = HtmlTagClose::peek(input.cursor()) { | ||
if open.ident.to_string() == next_close_ident.to_string() { | ||
if open_ident_str == next_close_ident.to_string() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recently learned that Ident implements PartialEq
so open.ident == next_close_ident
should be fine
// only '<' or '{ ... }' can follow. (that should be | ||
// enough for reasonable cases) | ||
// | ||
let next_punct = next_cursor.punct().is_some(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also check if punct.as_char() == '<'
#### Problem Self-closing tags are more convenient when there are no children. For instance, React's JSX allows it. Before, self-closing tags where considered as open tags in `verify_end`, causing codes like this to not compile: ```rust html! { <div> <div/> // <- considered as open tag </div> } ``` ``` error: this open tag has no corresponding close tag --> src/lib.rs:264:17 | ... | <div> | ^^^^^ ``` #### Solution Add a new `Peek`-able `HtmlSelfClosingTag`, used in `verify_end`. However, this fix isn't ideal because it peeks the buffer twice for non self-closing tags. I did it that way in order to keep the peek thing. An alternative would be to turn HtmlSelfClosingTag::peek into a function returning (ident, is_self_closing). #### Notes I added a TODO for when proc-macro::Diagnostic gets stabilized, in order to replace the clunky `eprintln!` with proper diagnostics. I didn't try to return a Result to get a nice error right now because this error should be fairly rare so we can just wait IMO. Fixes: yewstack#522
dd54b18
to
5908d33
Compare
Fixed 🙂 |
bors r+ |
523: html!: fix mixed self-closing and non-sc tags r=jstarry a=totorigolo Before, self-closing tags where considered as open tags in `verify_end`, causing codes like this to not compile: ```rust html! { <div> <div/> // <- considered as open tag </div> } ``` ``` error: this open tag has no corresponding close tag --> src/lib.rs:264:17 | ... | <div> | ^^^^^ ``` However, this fix isn't ideal because it peeks the buffer twice for non self-closing tags. I did it that way in order to keep the peek thing. An alternative would be to turn `HtmlSelfClosingTag::peek` into a standard function returning `(ident, is_self_closing)`. Fixes: #522 Co-authored-by: Thomas Lacroix <toto.rigolo@free.fr>
Thanks for fixing this! |
Build succeeded |
Before, self-closing tags where considered as open tags in
verify_end
, causing codes like this to not compile:However, this fix isn't ideal because it peeks the buffer twice for non self-closing tags. I did it that way in order to keep the peek thing. An alternative would be to turn
HtmlSelfClosingTag::peek
into a standard function returning(ident, is_self_closing)
.Fixes: #522