aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar René Kijewski <rene.kijewski@fu-berlin.de>2022-09-17 03:48:10 +0200
committerLibravatar Dirkjan Ochtman <dirkjan@ochtman.nl>2022-09-19 12:33:25 +0200
commit7edb3d4685aff00880906873db4eb9f649a077d9 (patch)
treebf9fe8d3c618d21556ea6a275e8b7e11ccc6b819
parent78db19000dd5a306b01cea4de1c3327b53ff2db4 (diff)
downloadaskama-7edb3d4685aff00880906873db4eb9f649a077d9.tar.gz
askama-7edb3d4685aff00880906873db4eb9f649a077d9.tar.bz2
askama-7edb3d4685aff00880906873db4eb9f649a077d9.zip
Add Expr::is_cachable()
-rw-r--r--askama_derive/src/generator.rs6
-rw-r--r--askama_derive/src/parser.rs33
2 files changed, 35 insertions, 4 deletions
diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs
index e8d15f6..7077cfd 100644
--- a/askama_derive/src/generator.rs
+++ b/askama_derive/src/generator.rs
@@ -7,7 +7,7 @@ use crate::CompileError;
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
-use std::collections::HashMap;
+use std::collections::hash_map::{Entry, HashMap};
use std::path::{Path, PathBuf};
use std::{cmp, hash, mem, str};
@@ -1196,11 +1196,9 @@ impl<'a> Generator<'a> {
expr_buf.buf, self.input.escaper
),
};
- let is_cacheable = !matches!(s, Expr::Call(..));
- use std::collections::hash_map::Entry;
let id = match expr_cache.entry(expression.clone()) {
- Entry::Occupied(e) if is_cacheable => *e.get(),
+ Entry::Occupied(e) if s.is_cachable() => *e.get(),
e => {
let id = self.named;
self.named += 1;
diff --git a/askama_derive/src/parser.rs b/askama_derive/src/parser.rs
index efcad73..dc163fa 100644
--- a/askama_derive/src/parser.rs
+++ b/askama_derive/src/parser.rs
@@ -106,6 +106,39 @@ impl Expr<'_> {
_ => false,
}
}
+
+ /// Returns `true` if the outcome of this expression may be used multiple times in the same
+ /// `write!()` call, without evaluating the expression again, i.e. the expression should be
+ /// side-effect free.
+ pub(crate) fn is_cachable(&self) -> bool {
+ match self {
+ // Literals are the definition of pure:
+ Expr::BoolLit(_) => true,
+ Expr::NumLit(_) => true,
+ Expr::StrLit(_) => true,
+ Expr::CharLit(_) => true,
+ // fmt::Display should have no effects:
+ Expr::Var(_) => true,
+ Expr::Path(_) => true,
+ // Check recursively:
+ Expr::Array(args) => args.iter().all(|arg| arg.is_cachable()),
+ Expr::Attr(lhs, _) => lhs.is_cachable(),
+ Expr::Index(lhs, rhs) => lhs.is_cachable() && rhs.is_cachable(),
+ Expr::Filter(_, args) => args.iter().all(|arg| arg.is_cachable()),
+ Expr::Unary(_, arg) => arg.is_cachable(),
+ Expr::BinOp(_, lhs, rhs) => lhs.is_cachable() && rhs.is_cachable(),
+ Expr::Range(_, lhs, rhs) => {
+ lhs.as_ref().map_or(true, |v| v.is_cachable())
+ && rhs.as_ref().map_or(true, |v| v.is_cachable())
+ }
+ Expr::Group(arg) => arg.is_cachable(),
+ Expr::Tuple(args) => args.iter().all(|arg| arg.is_cachable()),
+ // We have too little information to tell if the expression is pure:
+ Expr::Call(_, _) => false,
+ Expr::RustMacro(_, _) => false,
+ Expr::Try(_) => false,
+ }
+ }
}
pub(crate) type When<'a> = (Ws, Target<'a>, Vec<Node<'a>>);