aboutsummaryrefslogtreecommitdiffstats
path: root/askama_shared
diff options
context:
space:
mode:
Diffstat (limited to 'askama_shared')
-rw-r--r--askama_shared/src/generator.rs88
1 files changed, 84 insertions, 4 deletions
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs
index b5ab31c..50ea67a 100644
--- a/askama_shared/src/generator.rs
+++ b/askama_shared/src/generator.rs
@@ -586,6 +586,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
buf.write("{");
for (i, param) in params.iter().enumerate() {
if let Some(MatchParameter::Name(p)) = param.1 {
+ let p = normalize_identifier(p);
self.locals.insert_with_default(p);
} else {
self.locals.insert_with_default(param.0);
@@ -822,12 +823,14 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
buf.write("let ");
match *var {
Target::Name(name) => {
+ let name = normalize_identifier(name);
self.locals.insert_with_default(name);
buf.write(name);
}
Target::Tuple(ref targets) => {
buf.write("(");
for name in targets {
+ let name = normalize_identifier(name);
self.locals.insert_with_default(name);
buf.write(name);
buf.write(",");
@@ -851,6 +854,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
match *var {
Target::Name(name) => {
+ let name = normalize_identifier(name);
let meta = self.locals.get(&name).cloned();
let shadowed = matches!(&meta, Some(meta) if meta.initialized);
@@ -867,9 +871,10 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
self.locals.insert(name, LocalMeta::initialized());
}
Target::Tuple(ref targets) => {
- let shadowed = targets
- .iter()
- .any(|name| matches!(self.locals.get(&name), Some(meta) if meta.initialized));
+ let shadowed = targets.iter().any(|name| {
+ let name = normalize_identifier(name);
+ matches!(self.locals.get(&name), Some(meta) if meta.initialized)
+ });
if shadowed {
// Need to flush the buffer if the variable is being shadowed,
// to ensure the old variable is used.
@@ -878,6 +883,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
buf.write("let (");
for name in targets {
+ let name = normalize_identifier(name);
self.locals.insert(name, LocalMeta::initialized());
buf.write(name);
buf.write(",");
@@ -1442,7 +1448,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
return DisplayWrap::Unwrapped;
}
- buf.write(&self.locals.resolve_or_self(&s));
+ buf.write(normalize_identifier(&self.locals.resolve_or_self(&s)));
DisplayWrap::Unwrapped
}
@@ -1453,6 +1459,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
args: &[Expr],
) -> Result<DisplayWrap, CompileError> {
buf.write("(");
+ let s = normalize_identifier(s);
if !self.locals.contains(&s) && s != "self" {
buf.write("self.");
}
@@ -1486,12 +1493,14 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
fn visit_target(&mut self, buf: &mut Buffer, target: &'a Target) {
match *target {
Target::Name(name) => {
+ let name = normalize_identifier(name);
self.locals.insert_with_default(name);
buf.write(name);
}
Target::Tuple(ref targets) => {
buf.write("(");
for name in targets {
+ let name = normalize_identifier(name);
self.locals.insert_with_default(name);
buf.write(name);
buf.write(",");
@@ -1689,6 +1698,7 @@ where
impl MapChain<'_, &str, LocalMeta> {
fn resolve(&self, name: &str) -> Option<String> {
+ let name = normalize_identifier(name);
self.get(&name).map(|meta| match &meta.refs {
Some(expr) => expr.clone(),
None => name.to_string(),
@@ -1696,6 +1706,7 @@ impl MapChain<'_, &str, LocalMeta> {
}
fn resolve_or_self(&self, name: &str) -> String {
+ let name = normalize_identifier(name);
self.resolve(name)
.unwrap_or_else(|| format!("self.{}", name))
}
@@ -1732,3 +1743,72 @@ enum Writable<'a> {
Lit(&'a str),
Expr(&'a Expr<'a>),
}
+
+// Identifiers to be replaced with raw identifiers, so as to avoid
+// collisions between template syntax and Rust's syntax. In particular
+// [Rust keywords](https://doc.rust-lang.org/reference/keywords.html)
+// should be replaced, since they're not reserved words in Askama
+// syntax but have a high probability of causing problems in the
+// generated code.
+//
+// This list excludes the Rust keywords *self*, *Self*, and *super*
+// because they are not allowed to be raw identifiers, and *loop*
+// because it's used something like a keyword in the template
+// language.
+static USE_RAW: [(&str, &str); 47] = [
+ ("as", "r#as"),
+ ("break", "r#break"),
+ ("const", "r#const"),
+ ("continue", "r#continue"),
+ ("crate", "r#crate"),
+ ("else", "r#else"),
+ ("enum", "r#enum"),
+ ("extern", "r#extern"),
+ ("false", "r#false"),
+ ("fn", "r#fn"),
+ ("for", "r#for"),
+ ("if", "r#if"),
+ ("impl", "r#impl"),
+ ("in", "r#in"),
+ ("let", "r#let"),
+ ("match", "r#match"),
+ ("mod", "r#mod"),
+ ("move", "r#move"),
+ ("mut", "r#mut"),
+ ("pub", "r#pub"),
+ ("ref", "r#ref"),
+ ("return", "r#return"),
+ ("static", "r#static"),
+ ("struct", "r#struct"),
+ ("trait", "r#trait"),
+ ("true", "r#true"),
+ ("type", "r#type"),
+ ("unsafe", "r#unsafe"),
+ ("use", "r#use"),
+ ("where", "r#where"),
+ ("while", "r#while"),
+ ("async", "r#async"),
+ ("await", "r#await"),
+ ("dyn", "r#dyn"),
+ ("abstract", "r#abstract"),
+ ("become", "r#become"),
+ ("box", "r#box"),
+ ("do", "r#do"),
+ ("final", "r#final"),
+ ("macro", "r#macro"),
+ ("override", "r#override"),
+ ("priv", "r#priv"),
+ ("typeof", "r#typeof"),
+ ("unsized", "r#unsized"),
+ ("virtual", "r#virtual"),
+ ("yield", "r#yield"),
+ ("try", "r#try"),
+];
+
+fn normalize_identifier(ident: &str) -> &str {
+ if let Some(word) = USE_RAW.iter().find(|x| x.0 == ident) {
+ word.1
+ } else {
+ ident
+ }
+}