From ac8de6260e34c7dc7f7f2228e832d1860a31707d Mon Sep 17 00:00:00 2001
From: Matthew Taylor <wrapperup4@gmail.com>
Date: Mon, 24 Jul 2023 05:39:14 -0400
Subject: Fix Rust macro invocations not accepting a path (#837)

---
 askama_derive/src/generator.rs    |  6 +++---
 askama_derive/src/parser/expr.rs  | 38 +++++++++++++++++++++------------
 askama_derive/src/parser/tests.rs | 45 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+), 17 deletions(-)

(limited to 'askama_derive')

diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs
index d9ddfc8..f990330 100644
--- a/askama_derive/src/generator.rs
+++ b/askama_derive/src/generator.rs
@@ -1373,7 +1373,7 @@ impl<'a> Generator<'a> {
             }
             Expr::Group(ref inner) => self.visit_group(buf, inner)?,
             Expr::Call(ref obj, ref args) => self.visit_call(buf, obj, args)?,
-            Expr::RustMacro(name, args) => self.visit_rust_macro(buf, name, args),
+            Expr::RustMacro(ref path, args) => self.visit_rust_macro(buf, path, args),
             Expr::Try(ref expr) => self.visit_try(buf, expr.as_ref())?,
             Expr::Tuple(ref exprs) => self.visit_tuple(buf, exprs)?,
         })
@@ -1390,8 +1390,8 @@ impl<'a> Generator<'a> {
         Ok(DisplayWrap::Unwrapped)
     }
 
-    fn visit_rust_macro(&mut self, buf: &mut Buffer, name: &str, args: &str) -> DisplayWrap {
-        buf.write(name);
+    fn visit_rust_macro(&mut self, buf: &mut Buffer, path: &[&str], args: &str) -> DisplayWrap {
+        self.visit_path(buf, path);
         buf.write("!(");
         buf.write(args);
         buf.write(")");
diff --git a/askama_derive/src/parser/expr.rs b/askama_derive/src/parser/expr.rs
index fabaa34..1d4ea89 100644
--- a/askama_derive/src/parser/expr.rs
+++ b/askama_derive/src/parser/expr.rs
@@ -4,9 +4,10 @@ use nom::branch::alt;
 use nom::bytes::complete::{tag, take_till};
 use nom::character::complete::char;
 use nom::combinator::{cut, map, not, opt, peek, recognize};
+use nom::error::ErrorKind;
 use nom::multi::{fold_many0, many0, separated_list0, separated_list1};
 use nom::sequence::{delimited, pair, preceded, terminated, tuple};
-use nom::IResult;
+use nom::{error_position, IResult};
 
 use super::{
     bool_lit, char_lit, identifier, nested_parenthesis, not_ws, num_lit, path, str_lit, ws,
@@ -30,7 +31,7 @@ pub(crate) enum Expr<'a> {
     Group(Box<Expr<'a>>),
     Tuple(Vec<Expr<'a>>),
     Call(Box<Expr<'a>>, Vec<Expr<'a>>),
-    RustMacro(&'a str, &'a str),
+    RustMacro(Vec<&'a str>, &'a str),
     Try(Box<Expr<'a>>),
 }
 
@@ -183,7 +184,6 @@ fn expr_single(i: &str) -> IResult<&str, Expr<'_>> {
         expr_str_lit,
         expr_char_lit,
         expr_path,
-        expr_rust_macro,
         expr_array_lit,
         expr_var,
         expr_group,
@@ -194,6 +194,8 @@ enum Suffix<'a> {
     Attr(&'a str),
     Index(Expr<'a>),
     Call(Vec<Expr<'a>>),
+    // The value is the arguments of the macro call.
+    MacroCall(&'a str),
     Try,
 }
 
@@ -218,6 +220,16 @@ fn expr_call(i: &str) -> IResult<&str, Suffix<'_>> {
     map(arguments, Suffix::Call)(i)
 }
 
+fn expr_macro(i: &str) -> IResult<&str, Suffix<'_>> {
+    preceded(
+        pair(ws(char('!')), char('(')),
+        cut(terminated(
+            map(recognize(nested_parenthesis), Suffix::MacroCall),
+            char(')'),
+        )),
+    )(i)
+}
+
 fn expr_try(i: &str) -> IResult<&str, Suffix<'_>> {
     map(preceded(take_till(not_ws), char('?')), |_| Suffix::Try)(i)
 }
@@ -256,28 +268,26 @@ fn expr_prefix(i: &str) -> IResult<&str, Expr<'_>> {
 fn expr_suffix(i: &str) -> IResult<&str, Expr<'_>> {
     let (mut i, mut expr) = expr_single(i)?;
     loop {
-        let (j, suffix) = opt(alt((expr_attr, expr_index, expr_call, expr_try)))(i)?;
-        i = j;
+        let (j, suffix) = opt(alt((
+            expr_attr, expr_index, expr_call, expr_try, expr_macro,
+        )))(i)?;
         match suffix {
             Some(Suffix::Attr(attr)) => expr = Expr::Attr(expr.into(), attr),
             Some(Suffix::Index(index)) => expr = Expr::Index(expr.into(), index.into()),
             Some(Suffix::Call(args)) => expr = Expr::Call(expr.into(), args),
             Some(Suffix::Try) => expr = Expr::Try(expr.into()),
+            Some(Suffix::MacroCall(args)) => match expr {
+                Expr::Path(path) => expr = Expr::RustMacro(path, args),
+                Expr::Var(name) => expr = Expr::RustMacro(vec![name], args),
+                _ => return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))),
+            },
             None => break,
         }
+        i = j;
     }
     Ok((i, expr))
 }
 
-fn macro_arguments(i: &str) -> IResult<&str, &str> {
-    delimited(char('('), recognize(nested_parenthesis), char(')'))(i)
-}
-
-fn expr_rust_macro(i: &str) -> IResult<&str, Expr<'_>> {
-    let (i, (mname, _, args)) = tuple((identifier, char('!'), macro_arguments))(i)?;
-    Ok((i, Expr::RustMacro(mname, args)))
-}
-
 macro_rules! expr_prec_layer {
     ( $name:ident, $inner:ident, $op:expr ) => {
         fn $name(i: &str) -> IResult<&str, Expr<'_>> {
diff --git a/askama_derive/src/parser/tests.rs b/askama_derive/src/parser/tests.rs
index 91bb09b..801e787 100644
--- a/askama_derive/src/parser/tests.rs
+++ b/askama_derive/src/parser/tests.rs
@@ -221,6 +221,51 @@ fn test_parse_root_path() {
     );
 }
 
+#[test]
+fn test_rust_macro() {
+    let syntax = Syntax::default();
+    assert_eq!(
+        super::parse("{{ vec!(1, 2, 3) }}", &syntax).unwrap(),
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::RustMacro(vec!["vec"], "1, 2, 3",),
+        )],
+    );
+    assert_eq!(
+        super::parse("{{ alloc::vec!(1, 2, 3) }}", &syntax).unwrap(),
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::RustMacro(vec!["alloc", "vec"], "1, 2, 3",),
+        )],
+    );
+    assert_eq!(
+        super::parse("{{a!()}}", &syntax).unwrap(),
+        [Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))],
+    );
+    assert_eq!(
+        super::parse("{{a !()}}", &syntax).unwrap(),
+        [Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))],
+    );
+    assert_eq!(
+        super::parse("{{a! ()}}", &syntax).unwrap(),
+        [Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))],
+    );
+    assert_eq!(
+        super::parse("{{a ! ()}}", &syntax).unwrap(),
+        [Node::Expr(Ws(None, None), Expr::RustMacro(vec!["a"], ""))],
+    );
+    assert_eq!(
+        super::parse("{{A!()}}", &syntax).unwrap(),
+        [Node::Expr(Ws(None, None), Expr::RustMacro(vec!["A"], ""),)],
+    );
+    assert_eq!(
+        &*super::parse("{{a.b.c!( hello )}}", &syntax)
+            .unwrap_err()
+            .msg,
+        "problems parsing template source at row 1, column 7 near:\n\"!( hello )}}\"",
+    );
+}
+
 #[test]
 fn change_delimiters_parse_filter() {
     let syntax = Syntax {
-- 
cgit