diff --git a/rinja_derive/src/generator/expr.rs b/rinja_derive/src/generator/expr.rs index 7b1f69ab..129e7a2f 100644 --- a/rinja_derive/src/generator/expr.rs +++ b/rinja_derive/src/generator/expr.rs @@ -262,14 +262,9 @@ impl<'a> Generator<'a, '_> { "safe" => Self::_visit_safe_filter, "urlencode" | "urlencode_strict" => Self::_visit_urlencode_filter, name if BUILTIN_FILTERS.contains(&name) => Self::_visit_builtin_filter, - _ => return Self::_visit_custom_filter(self, ctx, buf, name, args, generics, node), + _ => Self::_visit_custom_filter, }; - if !generics.is_empty() { - return Err( - ctx.generate_error(format_args!("unexpected generics on filter `{name}`"), node) - ); - } - filter(self, ctx, buf, name, args, node) + filter(self, ctx, buf, name, args, generics, node) } fn _visit_custom_filter( @@ -298,9 +293,12 @@ impl<'a> Generator<'a, '_> { buf: &mut Buffer, name: &str, args: &[WithSpan<'_, Expr<'a>>], + generics: &[WithSpan<'_, TyGenerics<'_>>], _node: Span<'_>, ) -> Result { - buf.write(format_args!("rinja::filters::{name}(")); + buf.write(format_args!("rinja::filters::{name}")); + self.visit_call_generics(buf, generics); + buf.write('('); self._visit_args(ctx, buf, args)?; buf.write(")?"); Ok(DisplayWrap::Unwrapped) @@ -312,8 +310,10 @@ impl<'a> Generator<'a, '_> { buf: &mut Buffer, name: &str, args: &[WithSpan<'_, Expr<'a>>], + generics: &[WithSpan<'_, TyGenerics<'_>>], node: Span<'_>, ) -> Result { + ensure_filter_has_no_generics(ctx, name, generics, node)?; if cfg!(not(feature = "urlencode")) { return Err(ctx.generate_error( format_args!("the `{name}` filter requires the `urlencode` feature to be enabled"), @@ -336,8 +336,10 @@ impl<'a> Generator<'a, '_> { buf: &mut Buffer, name: &str, args: &[WithSpan<'_, Expr<'a>>], - _node: Span<'_>, + generics: &[WithSpan<'_, TyGenerics<'_>>], + node: Span<'_>, ) -> Result { + ensure_filter_has_no_generics(ctx, name, generics, node)?; // All filters return numbers, and any default formatted number is HTML safe. buf.write(format_args!( "rinja::filters::HtmlSafeOutput(rinja::filters::{name}(\ @@ -352,8 +354,9 @@ impl<'a> Generator<'a, '_> { &mut self, ctx: &Context<'_>, buf: &mut Buffer, - _name: &str, + name: &str, args: &[WithSpan<'_, Expr<'a>>], + generics: &[WithSpan<'_, TyGenerics<'_>>], node: Span<'_>, ) -> Result { const SINGULAR: &WithSpan<'static, Expr<'static>> = @@ -367,6 +370,7 @@ impl<'a> Generator<'a, '_> { content: "s", })); + ensure_filter_has_no_generics(ctx, name, generics, node)?; let (count, sg, pl) = match args { [count] => (count, SINGULAR, PLURAL), [count, sg] => (count, sg, PLURAL), @@ -398,8 +402,10 @@ impl<'a> Generator<'a, '_> { buf: &mut Buffer, name: &str, args: &[WithSpan<'_, Expr<'a>>], + generics: &[WithSpan<'_, TyGenerics<'_>>], node: Span<'_>, ) -> Result { + ensure_filter_has_no_generics(ctx, name, generics, node)?; ensure_filter_has_feature_alloc(ctx, name, node)?; if args.len() != 1 { return Err(ctx.generate_error( @@ -421,10 +427,12 @@ impl<'a> Generator<'a, '_> { &mut self, ctx: &Context<'_>, buf: &mut Buffer, - _name: &str, + name: &str, args: &[WithSpan<'_, Expr<'a>>], + generics: &[WithSpan<'_, TyGenerics<'_>>], node: Span<'_>, ) -> Result { + ensure_filter_has_no_generics(ctx, name, generics, node)?; let arg = match args { [arg] => arg, _ => return Err(ctx.generate_error("unexpected argument(s) in `as_ref` filter", node)), @@ -438,10 +446,12 @@ impl<'a> Generator<'a, '_> { &mut self, ctx: &Context<'_>, buf: &mut Buffer, - _name: &str, + name: &str, args: &[WithSpan<'_, Expr<'a>>], + generics: &[WithSpan<'_, TyGenerics<'_>>], node: Span<'_>, ) -> Result { + ensure_filter_has_no_generics(ctx, name, generics, node)?; let arg = match args { [arg] => arg, _ => return Err(ctx.generate_error("unexpected argument(s) in `deref` filter", node)), @@ -455,8 +465,9 @@ impl<'a> Generator<'a, '_> { &mut self, ctx: &Context<'_>, buf: &mut Buffer, - _name: &str, + name: &str, args: &[WithSpan<'_, Expr<'a>>], + generics: &[WithSpan<'_, TyGenerics<'_>>], node: Span<'_>, ) -> Result { if cfg!(not(feature = "serde_json")) { @@ -465,13 +476,13 @@ impl<'a> Generator<'a, '_> { node, )); } + ensure_filter_has_no_generics(ctx, name, generics, node)?; let filter = match args.len() { 1 => "json", 2 => "json_pretty", _ => return Err(ctx.generate_error("unexpected argument(s) in `json` filter", node)), }; - buf.write(format_args!("rinja::filters::{filter}(")); self._visit_args(ctx, buf, args)?; buf.write(")?"); @@ -482,10 +493,12 @@ impl<'a> Generator<'a, '_> { &mut self, ctx: &Context<'_>, buf: &mut Buffer, - _name: &str, + name: &str, args: &[WithSpan<'_, Expr<'a>>], + generics: &[WithSpan<'_, TyGenerics<'_>>], node: Span<'_>, ) -> Result { + ensure_filter_has_no_generics(ctx, name, generics, node)?; if args.len() != 1 { return Err(ctx.generate_error("unexpected argument(s) in `safe` filter", node)); } @@ -499,10 +512,12 @@ impl<'a> Generator<'a, '_> { &mut self, ctx: &Context<'_>, buf: &mut Buffer, - _name: &str, + name: &str, args: &[WithSpan<'_, Expr<'a>>], + generics: &[WithSpan<'_, TyGenerics<'_>>], node: Span<'_>, ) -> Result { + ensure_filter_has_no_generics(ctx, name, generics, node)?; if args.len() > 2 { return Err(ctx.generate_error("only two arguments allowed to escape filter", node)); } @@ -562,8 +577,10 @@ impl<'a> Generator<'a, '_> { buf: &mut Buffer, name: &str, args: &[WithSpan<'_, Expr<'a>>], + generics: &[WithSpan<'_, TyGenerics<'_>>], node: Span<'_>, ) -> Result { + ensure_filter_has_no_generics(ctx, name, generics, node)?; ensure_filter_has_feature_alloc(ctx, name, node)?; if !args.is_empty() { if let Expr::StrLit(ref fmt) = *args[0] { @@ -586,8 +603,10 @@ impl<'a> Generator<'a, '_> { buf: &mut Buffer, name: &str, args: &[WithSpan<'_, Expr<'a>>], + generics: &[WithSpan<'_, TyGenerics<'_>>], node: Span<'_>, ) -> Result { + ensure_filter_has_no_generics(ctx, name, generics, node)?; ensure_filter_has_feature_alloc(ctx, name, node)?; if let [_, arg2] = args { if let Expr::StrLit(ref fmt) = **arg2 { @@ -607,10 +626,12 @@ impl<'a> Generator<'a, '_> { &mut self, ctx: &Context<'_>, buf: &mut Buffer, - _name: &str, + name: &str, args: &[WithSpan<'_, Expr<'a>>], - _node: Span<'_>, + generics: &[WithSpan<'_, TyGenerics<'_>>], + node: Span<'_>, ) -> Result { + ensure_filter_has_no_generics(ctx, name, generics, node)?; buf.write("rinja::filters::join((&"); for (i, arg) in args.iter().enumerate() { if i > 0 { @@ -760,8 +781,12 @@ impl<'a> Generator<'a, '_> { } fn visit_ty_generic(&mut self, buf: &mut Buffer, generic: &WithSpan<'_, TyGenerics<'_>>) { - buf.write(normalize_identifier(generic.ty)); - self.visit_ty_generics(buf, &generic.generics); + let TyGenerics { refs, ty, generics } = &**generic; + for _ in 0..*refs { + buf.write('&'); + } + buf.write(normalize_identifier(ty)); + self.visit_ty_generics(buf, generics); } fn visit_index( @@ -1119,6 +1144,19 @@ impl<'a> Generator<'a, '_> { } } +fn ensure_filter_has_no_generics( + ctx: &Context<'_>, + name: &str, + generics: &[WithSpan<'_, TyGenerics<'_>>], + node: Span<'_>, +) -> Result<(), CompileError> { + if generics.is_empty() { + Ok(()) + } else { + Err(ctx.generate_error(format_args!("unexpected generics on filter `{name}`"), node)) + } +} + fn ensure_filter_has_feature_alloc( ctx: &Context<'_>, name: &str,