From 26a9d7484d41e8c7c10fde0039302424f9844b0d Mon Sep 17 00:00:00 2001
From: Chris Wong <lambda.fairy@gmail.com>
Date: Mon, 15 Jan 2024 00:45:49 +1100
Subject: [PATCH] Disallow int literals in attribute values (#415)

---
 maud/tests/warnings/non-string-literal.stderr | 12 ++++++++++++
 maud_macros/src/ast.rs                        |  5 -----
 maud_macros/src/parse.rs                      | 13 ++++++++-----
 3 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/maud/tests/warnings/non-string-literal.stderr b/maud/tests/warnings/non-string-literal.stderr
index 7755e3f..d0a128b 100644
--- a/maud/tests/warnings/non-string-literal.stderr
+++ b/maud/tests/warnings/non-string-literal.stderr
@@ -1,3 +1,15 @@
+error: literal must be double-quoted: `"42"`
+ --> tests/warnings/non-string-literal.rs:5:9
+  |
+5 |         42
+  |         ^^
+
+error: literal must be double-quoted: `"42usize"`
+ --> tests/warnings/non-string-literal.rs:6:9
+  |
+6 |         42usize
+  |         ^^^^^^^
+
 error: literal must be double-quoted: `"42.0"`
  --> tests/warnings/non-string-literal.rs:7:9
   |
diff --git a/maud_macros/src/ast.rs b/maud_macros/src/ast.rs
index 16d228e..b95665e 100644
--- a/maud_macros/src/ast.rs
+++ b/maud_macros/src/ast.rs
@@ -223,11 +223,6 @@ pub fn name_to_string(name: TokenStream) -> String {
             if let TokenTree::Literal(literal) = token {
                 match Lit::new(literal.clone()) {
                     Lit::Str(str) => str.value(),
-                    Lit::Char(char) => char.value().to_string(),
-                    Lit::ByteStr(byte) => {
-                        String::from_utf8(byte.value()).expect("Invalid utf8 byte")
-                    }
-                    Lit::Byte(byte) => (byte.value() as char).to_string(),
                     _ => literal.to_string(),
                 }
             } else {
diff --git a/maud_macros/src/parse.rs b/maud_macros/src/parse.rs
index 1b85d1f..05af289 100644
--- a/maud_macros/src/parse.rs
+++ b/maud_macros/src/parse.rs
@@ -94,7 +94,7 @@ impl Parser {
             // Literal
             TokenTree::Literal(literal) => {
                 self.advance();
-                self.literal(literal)
+                self.literal(literal, false)
             }
             // Special form
             TokenTree::Punct(ref punct) if punct.as_char() == '@' => {
@@ -194,7 +194,10 @@ impl Parser {
     }
 
     /// Parses a literal string.
-    fn literal(&mut self, literal: Literal) -> ast::Markup {
+    ///
+    /// If `allow_int_literal` is `true`, then integer literals (like `123`) will be accepted and
+    /// returned.
+    fn literal(&mut self, literal: Literal, allow_int_literal: bool) -> ast::Markup {
         match Lit::new(literal.clone()) {
             Lit::Str(lit_str) => {
                 return ast::Markup::Literal {
@@ -204,13 +207,13 @@ impl Parser {
             }
             // Boolean literals are idents, so `Lit::Bool` is handled in
             // `markup`, not here.
-            Lit::Int(lit_int) => {
+            Lit::Int(lit_int) if allow_int_literal => {
                 return ast::Markup::Literal {
                     content: lit_int.to_string(),
                     span: SpanRange::single_span(literal.span()),
                 };
             }
-            Lit::Float(..) => {
+            Lit::Int(..) | Lit::Float(..) => {
                 emit_error!(literal, r#"literal must be double-quoted: `"{}"`"#, literal);
             }
             Lit::Char(lit_char) => {
@@ -722,7 +725,7 @@ impl Parser {
                     false
                 }
                 Some(TokenTree::Literal(ref literal)) if expect_ident_or_literal => {
-                    self.literal(literal.clone());
+                    self.literal(literal.clone(), true);
                     self.advance();
                     result.push(TokenTree::Literal(literal.clone()));
                     false