From 37a6e9200f628980a8b3e7cdb4b11599db3a5946 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 31 Oct 2024 14:48:25 -0500 Subject: [PATCH] Fix if chain attributes with mixed expressions and strings (#3149) * Fix if chain attributes with mixed expressions and strings --- .../tests/conditional_formatted_attributes.rs | 19 +++++++ packages/rsx/src/attribute.rs | 50 ++++++++++++++++--- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/packages/core/tests/conditional_formatted_attributes.rs b/packages/core/tests/conditional_formatted_attributes.rs index c96e8d33d..cd0e860ec 100644 --- a/packages/core/tests/conditional_formatted_attributes.rs +++ b/packages/core/tests/conditional_formatted_attributes.rs @@ -10,4 +10,23 @@ fn partially_formatted_conditional_attribute() { 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}" }, + } + }; } diff --git a/packages/rsx/src/attribute.rs b/packages/rsx/src/attribute.rs index f6430229a..ec2e90084 100644 --- a/packages/rsx/src/attribute.rs +++ b/packages/rsx/src/attribute.rs @@ -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> { 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 { condition, then_value, @@ -619,15 +638,29 @@ impl IfAttributeValue { // 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 // This fixes https://github.com/DioxusLabs/dioxus/issues/2997 - fn quote_attribute_value_string(value: &AttributeValue) -> TokenStream2 { - if matches!(value, AttributeValue::AttrLiteral(HotLiteral::Fmted(_))) { - quote! { #value.to_string() } + fn quote_attribute_value_string( + value: &AttributeValue, + 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 { 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 { quote! { #then_value } @@ -640,11 +673,11 @@ impl IfAttributeValue { let else_value = match else_value.as_deref() { Some(AttributeValue::IfExpr(else_value)) => { 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 } Some(other) => { - let other = quote_attribute_value_string(other); + let other = quote_attribute_value_string(other, contains_expression); if terminated { quote! { #other } } else { @@ -709,7 +742,8 @@ impl ToTokens for IfAttributeValue { fn to_tokens(&self, tokens: &mut TokenStream2) { // If the if expression is terminated, we can just return the then value 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) } }