From 41aea5f92c9f3244319efd64ce1ff88220f2e507 Mon Sep 17 00:00:00 2001
From: Chris Wong <lambda.fairy@gmail.com>
Date: Sat, 16 Jun 2018 21:33:23 +1200
Subject: [PATCH] Require braces around element bodies

---
 maud/tests/basic_syntax.rs       | 20 +++++++-------------
 maud/tests/control_structures.rs | 22 ++++++++++++++--------
 maud_extras/lib.rs               |  2 +-
 maud_macros/src/parse.rs         | 17 ++++++++++++-----
 4 files changed, 34 insertions(+), 27 deletions(-)

diff --git a/maud/tests/basic_syntax.rs b/maud/tests/basic_syntax.rs
index 49b5bc6..8c33dc7 100644
--- a/maud/tests/basic_syntax.rs
+++ b/maud/tests/basic_syntax.rs
@@ -50,12 +50,6 @@ fn simple_elements() {
     assert_eq!(s, "<p><b>pickle</b>barrel<i>kumquat</i></p>");
 }
 
-#[test]
-fn nesting_elements() {
-    let s = html!(html body div p sup "butts").into_string();
-    assert_eq!(s, "<html><body><div><p><sup>butts</sup></p></div></body></html>");
-}
-
 #[test]
 fn empty_elements() {
     let s = html!("pinkie" br; "pie").into_string();
@@ -73,7 +67,7 @@ fn simple_attributes() {
     let s = html! {
         link rel="stylesheet" href="styles.css";
         section id="midriff" {
-            p class="hotpink" "Hello!"
+            p class="hotpink" { "Hello!" }
         }
     }.into_string();
     assert_eq!(s, concat!(
@@ -83,7 +77,7 @@ fn simple_attributes() {
 
 #[test]
 fn empty_attributes() {
-    let s = html!(div readonly? input type="checkbox" checked?;).into_string();
+    let s = html!(div readonly? { input type="checkbox" checked?; }).into_string();
     assert_eq!(s, r#"<div readonly><input type="checkbox" checked></div>"#);
 }
 
@@ -112,7 +106,7 @@ fn toggle_empty_attributes_braces() {
 
 #[test]
 fn colons_in_names() {
-    let s = html!(pon-pon:controls-alpha a on:click="yay()" "Yay!").into_string();
+    let s = html!(pon-pon:controls-alpha { a on:click="yay()" { "Yay!" } }).into_string();
     assert_eq!(s, concat!(
             r#"<pon-pon:controls-alpha>"#,
             r#"<a on:click="yay()">Yay!</a>"#,
@@ -151,14 +145,14 @@ fn classes_shorthand() {
 
 #[test]
 fn hyphens_in_class_names() {
-    let s = html!(p.rocks-these.are--my--rocks "yes").into_string();
+    let s = html!(p.rocks-these.are--my--rocks { "yes" }).into_string();
     assert_eq!(s, r#"<p class="rocks-these are--my--rocks">yes</p>"#);
 }
 
 #[test]
 fn toggle_classes() {
     fn test(is_cupcake: bool, is_muffin: bool) -> Markup {
-        html!(p.cupcake[is_cupcake].muffin[is_muffin] "Testing!")
+        html!(p.cupcake[is_cupcake].muffin[is_muffin] { "Testing!" })
     }
     assert_eq!(test(true, true).into_string(), r#"<p class="cupcake muffin">Testing!</p>"#);
     assert_eq!(test(false, true).into_string(), r#"<p class=" muffin">Testing!</p>"#);
@@ -169,14 +163,14 @@ fn toggle_classes() {
 #[test]
 fn toggle_classes_braces() {
     struct Maud { rocks: bool }
-    let s = html!(p.rocks[Maud { rocks: true }.rocks] "Awesome!").into_string();
+    let s = html!(p.rocks[Maud { rocks: true }.rocks] { "Awesome!" }).into_string();
     assert_eq!(s, r#"<p class="rocks">Awesome!</p>"#);
 }
 
 #[test]
 fn mixed_classes() {
     fn test(is_muffin: bool) -> Markup {
-        html!(p.cupcake.muffin[is_muffin].lamington "Testing!")
+        html!(p.cupcake.muffin[is_muffin].lamington { "Testing!" })
     }
     assert_eq!(test(true).into_string(), r#"<p class="cupcake lamington muffin">Testing!</p>"#);
     assert_eq!(test(false).into_string(), r#"<p class="cupcake lamington">Testing!</p>"#);
diff --git a/maud/tests/control_structures.rs b/maud/tests/control_structures.rs
index 028737d..e27164e 100644
--- a/maud/tests/control_structures.rs
+++ b/maud/tests/control_structures.rs
@@ -44,8 +44,10 @@ fn if_let() {
 fn while_expr() {
     let mut numbers = (0..3).into_iter().peekable();
     let s = html! {
-        ul @while numbers.peek().is_some() {
-            li (numbers.next().unwrap())
+        ul {
+            @while numbers.peek().is_some() {
+                li { (numbers.next().unwrap()) }
+            }
         }
     }.into_string();
     assert_eq!(s, "<ul><li>0</li><li>1</li><li>2</li></ul>");
@@ -56,8 +58,10 @@ fn while_let_expr() {
     let mut numbers = (0..3).into_iter();
     #[cfg_attr(feature = "cargo-clippy", allow(while_let_on_iterator))]
     let s = html! {
-        ul @while let Some(n) = numbers.next() {
-            li (n)
+        ul {
+            @while let Some(n) = numbers.next() {
+                li { (n) }
+            }
         }
     }.into_string();
     assert_eq!(s, "<ul><li>0</li><li>1</li><li>2</li></ul>");
@@ -67,8 +71,10 @@ fn while_let_expr() {
 fn for_expr() {
     let ponies = ["Apple Bloom", "Scootaloo", "Sweetie Belle"];
     let s = html! {
-        ul @for pony in &ponies {
-            li (pony)
+        ul {
+            @for pony in &ponies {
+                li { (pony) }
+            }
         }
     }.into_string();
     assert_eq!(s, concat!(
@@ -85,7 +91,7 @@ fn match_expr() {
         let s = html! {
             @match input {
                 Some(value) => {
-                    div (value)
+                    div { (value) }
                 },
                 None => {
                     "oh noes"
@@ -102,7 +108,7 @@ fn match_expr_without_delims() {
         let s = html! {
             @match input {
                 Some(value) => (value),
-                None => span "oh noes",
+                None => span { "oh noes" },
             }
         }.into_string();
         assert_eq!(s, output);
diff --git a/maud_extras/lib.rs b/maud_extras/lib.rs
index 00cf892..777a852 100644
--- a/maud_extras/lib.rs
+++ b/maud_extras/lib.rs
@@ -112,7 +112,7 @@ pub struct Title<T: AsRef<str>>(pub T);
 impl<T: AsRef<str>> Render for Title<T> {
     fn render(&self) -> Markup {
         html! {
-            title (self.0.as_ref())
+            title { (self.0.as_ref()) }
         }
     }
 }
diff --git a/maud_macros/src/parse.rs b/maud_macros/src/parse.rs
index ef3c260..9b76c2d 100644
--- a/maud_macros/src/parse.rs
+++ b/maud_macros/src/parse.rs
@@ -422,11 +422,18 @@ impl Parser {
             _ => {
                 match self.markup()? {
                     ast::Markup::Block(block) => ast::ElementBody::Block { block },
-                    markup => ast::ElementBody::Block {
-                        block: ast::Block {
-                            markups: vec![markup],
-                            outer_span: Span::call_site(),
-                        },
+                    markup => {
+                        let markup_span = markup.span();
+                        markup_span
+                            .error("element body must be wrapped in braces")
+                            .help("see https://github.com/lfairy/maud/pull/137 for details")
+                            .emit();
+                        ast::ElementBody::Block {
+                            block: ast::Block {
+                                markups: vec![markup],
+                                outer_span: markup_span,
+                            },
+                        }
                     },
                 }
             },