Fix if chain attributes with mixed expressions and strings (#3149)

* Fix if chain attributes with mixed expressions and strings
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-10-31 12:48:25 -07:00
1 parent 292f757d5d
commit 37a6e9200f
2 files changed
+61 -8

No files matched your search

@@ -10,4 +10,23 @@ fn partially_formatted_conditional_attribute() {
width: if true { "{width}" } else { "100px" } width: if true { "{width}" } else { "100px" }
} }
}; };
// And make sure it works if one of those branches is an expression
// Regression test for https://github.com/DioxusLabs/dioxus/issues/3146
let opt = "button";
_ = rsx! {
input {
type: if true { opt } else { "text" },
}
input {
type: if true { opt.to_string() } else { "text with" },
}
input {
type: if true { opt.to_string() } else { "text with {width}" },
}
input {
type: if true { opt.to_string() } else if true { "" } else { "text with {width}" },
}
};
} }
+42 -8
View File
@@ -582,6 +582,20 @@ impl IfAttributeValue {
} }
} }
fn contains_expression(&self) -> bool {
if let AttributeValue::AttrExpr(_) = &*self.then_value {
return true;
}
match &self.else_value {
Some(attribute) => match attribute.as_ref() {
AttributeValue::IfExpr(if_expr) => if_expr.is_terminated(),
AttributeValue::AttrExpr(_) => true,
_ => false,
},
None => false,
}
}
fn parse_attribute_value_from_block(block: &Block) -> syn::Result<Box<AttributeValue>> { fn parse_attribute_value_from_block(block: &Block) -> syn::Result<Box<AttributeValue>> {
let stmts = &block.stmts; let stmts = &block.stmts;
@@ -609,7 +623,12 @@ impl IfAttributeValue {
} }
} }
fn to_tokens_with_terminated(&self, tokens: &mut TokenStream2, terminated: bool) { fn to_tokens_with_terminated(
&self,
tokens: &mut TokenStream2,
terminated: bool,
contains_expression: bool,
) {
let IfAttributeValue { let IfAttributeValue {
condition, condition,
then_value, then_value,
@@ -619,15 +638,29 @@ impl IfAttributeValue {
// Quote an attribute value and convert the value to a string if it is formatted // Quote an attribute value and convert the value to a string if it is formatted
// We always quote formatted segments as strings inside if statements so they have a consistent type // We always quote formatted segments as strings inside if statements so they have a consistent type
// This fixes https://github.com/DioxusLabs/dioxus/issues/2997 // This fixes https://github.com/DioxusLabs/dioxus/issues/2997
fn quote_attribute_value_string(value: &AttributeValue) -> TokenStream2 { fn quote_attribute_value_string(
if matches!(value, AttributeValue::AttrLiteral(HotLiteral::Fmted(_))) { value: &AttributeValue,
quote! { #value.to_string() } contains_expression: bool,
) -> TokenStream2 {
if let AttributeValue::AttrLiteral(HotLiteral::Fmted(fmted)) = value {
if let Some(str) = fmted.to_static().filter(|_| contains_expression) {
// If this is actually a static string, the user may be using a static string expression in another branch
// use into to convert the string to whatever the other branch is using
quote! {
{
#[allow(clippy::useless_conversion)]
#str.into()
}
}
} else {
quote! { #value.to_string() }
}
} else { } else {
value.to_token_stream() value.to_token_stream()
} }
} }
let then_value = quote_attribute_value_string(then_value); let then_value = quote_attribute_value_string(then_value, terminated);
let then_value = if terminated { let then_value = if terminated {
quote! { #then_value } quote! { #then_value }
@@ -640,11 +673,11 @@ impl IfAttributeValue {
let else_value = match else_value.as_deref() { let else_value = match else_value.as_deref() {
Some(AttributeValue::IfExpr(else_value)) => { Some(AttributeValue::IfExpr(else_value)) => {
let mut tokens = TokenStream2::new(); let mut tokens = TokenStream2::new();
else_value.to_tokens_with_terminated(&mut tokens, terminated); else_value.to_tokens_with_terminated(&mut tokens, terminated, contains_expression);
tokens tokens
} }
Some(other) => { Some(other) => {
let other = quote_attribute_value_string(other); let other = quote_attribute_value_string(other, contains_expression);
if terminated { if terminated {
quote! { #other } quote! { #other }
} else { } else {
@@ -709,7 +742,8 @@ impl ToTokens for IfAttributeValue {
fn to_tokens(&self, tokens: &mut TokenStream2) { fn to_tokens(&self, tokens: &mut TokenStream2) {
// If the if expression is terminated, we can just return the then value // If the if expression is terminated, we can just return the then value
let terminated = self.is_terminated(); let terminated = self.is_terminated();
self.to_tokens_with_terminated(tokens, terminated) let contains_expression = self.contains_expression();
self.to_tokens_with_terminated(tokens, terminated, contains_expression)
} }
} }