Repository: djc/askama
Pull request #929 (2023-12-07 09:15:44)

Prepare askama_derive and _parser releases
cc @kotx


Patch:
diff --git a/askama_derive/Cargo.toml b/askama_derive/Cargo.toml
index 53cdc8b..fb7db70 100644
--- a/askama_derive/Cargo.toml
+++ b/askama_derive/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "askama_derive"
-version = "0.12.2"
+version = "0.12.3"
 description = "Procedural macro package for Askama"
 homepage = "https://github.com/djc/askama"
 repository = "https://github.com/djc/askama"
@@ -31,7 +31,7 @@ with-tide = []
 with-warp = []
 
 [dependencies]
-parser = { package = "askama_parser", version = "0.1", path = "../askama_parser" }
+parser = { package = "askama_parser", version = "0.2", path = "../askama_parser" }
 mime = "0.3"
 mime_guess = "2"
 proc-macro2 = "1"
diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs
index 65136c6..df15bdc 100644
--- a/askama_derive/src/generator.rs
+++ b/askama_derive/src/generator.rs
@@ -916,7 +916,14 @@ impl<'a> Generator<'a> {
         };
 
         let mut expr_buf = Buffer::new(0);
+        let borrow_val = !is_copyable(val);
+        if borrow_val {
+            expr_buf.write("&(");
+        }
         self.visit_expr(&mut expr_buf, val)?;
+        if borrow_val {
+            expr_buf.write(")");
+        }
 
         let shadowed = self.is_shadowing_variable(&l.var)?;
         if shadowed {
@@ -1945,7 +1952,7 @@ fn is_copyable_within_op(expr: &Expr<'_>, within_op: bool) -> bool {
 pub(crate) fn is_attr_self(expr: &Expr<'_>) -> bool {
     match expr {
         Expr::Attr(obj, _) if matches!(obj.as_ref(), Expr::Var("self")) => true,
-        Expr::Attr(obj, _) if matches!(obj.as_ref(), Expr::Attr(..)) => is_attr_self(expr),
+        Expr::Attr(obj, _) if matches!(obj.as_ref(), Expr::Attr(..)) => is_attr_self(obj),
         _ => false,
     }
 }
diff --git a/askama_parser/Cargo.toml b/askama_parser/Cargo.toml
index e2fb11c..eecf32e 100644
--- a/askama_parser/Cargo.toml
+++ b/askama_parser/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "askama_parser"
-version = "0.1.1"
+version = "0.2.0"
 description = "Parser for Askama templates"
 documentation = "https://docs.rs/askama"
 keywords = ["markup", "template", "jinja2", "html"]
diff --git a/askama_parser/src/expr.rs b/askama_parser/src/expr.rs
index 2928b57..65a7835 100644
--- a/askama_parser/src/expr.rs
+++ b/askama_parser/src/expr.rs
@@ -119,7 +119,7 @@ impl<'a> Expr<'a> {
                         }
                     }),
                 ),
-                char(')'),
+                tuple((opt(ws(char(','))), char(')'))),
             )),
         )(i)
     }
diff --git a/askama_parser/src/node.rs b/askama_parser/src/node.rs
index d11895b..c2f7b39 100644
--- a/askama_parser/src/node.rs
+++ b/askama_parser/src/node.rs
@@ -224,7 +224,8 @@ impl<'a> Target<'a> {
         }
 
         // neither literal nor struct nor path
-        map(identifier, Self::Name)(i)
+        let (new_i, name) = identifier(i)?;
+        Ok((new_i, Self::verify_name(i, name)?))
     }
 
     fn lit(i: &'a str) -> ParseResult<'a, Self> {
@@ -236,9 +237,24 @@ impl<'a> Target<'a> {
         ))(i)
     }
 
-    fn named(i: &'a str) -> ParseResult<'a, (&str, Self)> {
-        let (i, (src, target)) = pair(identifier, opt(preceded(ws(char(':')), Self::parse)))(i)?;
-        Ok((i, (src, target.unwrap_or(Self::Name(src)))))
+    fn named(init_i: &'a str) -> ParseResult<'a, (&str, Self)> {
+        let (i, (src, target)) =
+            pair(identifier, opt(preceded(ws(char(':')), Self::parse)))(init_i)?;
+        let target = match target {
+            Some(target) => target,
+            None => Self::verify_name(init_i, src)?,
+        };
+        Ok((i, (src, target)))
+    }
+
+    fn verify_name(input: &'a str, name: &'a str) -> Result<Self, nom::Err<ErrorContext<'a>>> {
+        match name {
+            "self" | "writer" => Err(nom::Err::Failure(ErrorContext {
+                input,
+                message: Some(Cow::Owned(format!("Cannot use `{name}` as a name"))),
+            })),
+            _ => Ok(Self::Name(name)),
+        }
     }
 }
 
@@ -477,7 +493,7 @@ impl<'a> Macro<'a> {
             delimited(
                 ws(char('(')),
                 separated_list0(char(','), ws(identifier)),
-                ws(char(')')),
+                tuple((opt(ws(char(','))), char(')'))),
             )(i)
         }
 


Test patch:
diff --git a/testing/tests/calls.rs b/testing/tests/calls.rs
index 052f34f..28d7491 100644
--- a/testing/tests/calls.rs
+++ b/testing/tests/calls.rs
@@ -80,3 +80,22 @@ fn test_one_func_binop() {
     };
     assert_eq!(t.render().unwrap(), "246");
 }
+
+fn double_attr_arg_helper(x: u32) -> u32 {
+    x * x + x
+}
+
+#[derive(askama::Template)]
+#[template(
+    source = "{{ self::double_attr_arg_helper(self.x.0 + 2) }}",
+    ext = "txt"
+)]
+struct DoubleAttrArg {
+    x: (u32,),
+}
+
+#[test]
+fn test_double_attr_arg() {
+    let t = DoubleAttrArg { x: (10,) };
+    assert_eq!(t.render().unwrap(), "156");
+}
diff --git a/testing/tests/macro.rs b/testing/tests/macro.rs
index 9b3c63d..3d027d2 100644
--- a/testing/tests/macro.rs
+++ b/testing/tests/macro.rs
@@ -140,3 +140,33 @@ struct OnlyNamedArgument;
 fn test_only_named_argument() {
     assert_eq!(OnlyNamedArgument.render().unwrap(), "hi");
 }
+
+// Check for trailing commas.
+#[derive(Template)]
+#[template(
+    source = r#"{% macro button(label , ) %}
+{{- label -}}
+{% endmacro %}
+{%- macro button2(label ,) %}
+{% endmacro %}
+{%- macro button3(label,) %}
+{% endmacro %}
+{%- macro button4(label, ) %}
+{% endmacro %}
+{%- macro button5(label ) %}
+{% endmacro %}
+
+{%- call button(label="hi" , ) -%}
+{%- call button(label="hi" ,) -%}
+{%- call button(label="hi",) -%}
+{%- call button(label="hi", ) -%}
+{%- call button(label="hi" ) -%}
+"#,
+    ext = "html"
+)]
+struct TrailingComma;
+
+#[test]
+fn test_trailing_comma() {
+    assert_eq!(TrailingComma.render().unwrap(), "hihihihihi");
+}
diff --git a/testing/tests/simple.rs b/testing/tests/simple.rs
index f0f0a26..55353f5 100644
--- a/testing/tests/simple.rs
+++ b/testing/tests/simple.rs
@@ -484,3 +484,17 @@ fn test_num_literals() {
         "[90, -90, 90, 2, 56, 240, 10.5, 10.5, 100000000000, 105000000000]",
     );
 }
+
+#[derive(askama::Template)]
+#[template(source = "{% let word = s %}{{ word }}", ext = "html")]
+struct LetBorrow {
+    s: String,
+}
+
+#[test]
+fn test_let_borrow() {
+    let template = LetBorrow {
+        s: "hello".to_owned(),
+    };
+    assert_eq!(template.render().unwrap(), "hello")
+}