Skip to content

Commit 71d85e6

Browse files
authored
Fix parsing of braced expressions followed by a method (#4035)
* Fix parsing of braced expressions followed by a method * Improves the logic for parsing braced expressions * Fix hot-reload issue * Added cases for braced expressions followed by identifiers or string literals
1 parent f43a8c6 commit 71d85e6

File tree

2 files changed

+53
-3
lines changed

2 files changed

+53
-3
lines changed

packages/rsx/src/raw_expr.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use proc_macro2::TokenStream as TokenStream2;
1+
use proc_macro2::{Delimiter, TokenStream as TokenStream2, TokenTree};
22
use quote::ToTokens;
33
use std::hash;
44
use syn::{parse::Parse, spanned::Spanned, token::Brace, Expr};
@@ -15,8 +15,33 @@ pub struct PartialExpr {
1515

1616
impl Parse for PartialExpr {
1717
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
18-
// Parse as an expression if there is no brace
19-
if !input.peek(syn::token::Brace) {
18+
// Input is a braced expression if it's a braced group
19+
// followed by either of the following:
20+
// - the end of the stream
21+
// - a comma
22+
// - another braced group
23+
// - an identifier
24+
// - a string literal
25+
let mut is_braced = false;
26+
if let Some((TokenTree::Group(group), next)) = input.fork().cursor().token_tree() {
27+
let next_char_is_a_comma = next.punct().is_some_and(|(tt, _)| tt.as_char() == ',');
28+
let next_is_a_braced_exp = next.group(Delimiter::Brace).is_some();
29+
let next_is_an_ident = next.ident().is_some();
30+
let next_is_a_string_literal = next.literal().is_some();
31+
32+
if group.delimiter() == Delimiter::Brace
33+
&& (next.eof()
34+
|| next_char_is_a_comma
35+
|| next_is_a_braced_exp
36+
|| next_is_an_ident
37+
|| next_is_a_string_literal)
38+
{
39+
is_braced = true
40+
}
41+
};
42+
43+
// Parse as an expression if it's not braced
44+
if !is_braced {
2045
let expr = input.parse::<syn::Expr>()?;
2146
return Ok(Self {
2247
brace: None,

packages/rsx/tests/parsing.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ fn complex_kitchen_sink() {
7171
// complex_carry
7272
button {
7373
class: "flex items-center pl-3 py-3 pr-2 text-gray-500 hover:bg-indigo-50 rounded",
74+
width: {"100%"}.to_string(),
7475
onclick: move |evt| {
7576
show_user_menu.set(!show_user_menu.get());
7677
evt.cancel_bubble();
@@ -167,3 +168,27 @@ fn key_cannot_be_static() {
167168
println!("{:?}", parsed.body.diagnostics);
168169
assert!(!parsed.body.diagnostics.is_empty());
169170
}
171+
172+
#[test]
173+
fn braced_expressions() {
174+
let item = quote::quote! {
175+
div {
176+
width: {100} - 50,
177+
width: {"100%"}.to_string(),
178+
width: {|| "100%"}(),
179+
}
180+
// Partial expressions in braces rsx should be allowed and output as-is
181+
// for autocomplete
182+
{partial.}
183+
div {}
184+
{partial.}
185+
// Comments should be ignored
186+
div {}
187+
{partial.}
188+
"hello world"
189+
{partial.}
190+
if true {}
191+
};
192+
193+
let _cb: CallBody = syn::parse2(item).unwrap();
194+
}

0 commit comments

Comments
 (0)