mirror of
https://github.com/askama-rs/askama.git
synced 2025-10-01 15:00:57 +00:00
Use raw identifiers for identifiers which collide with Rust keywords (#476)
Co-authored-by: Daniel Arbuckle <djarb@highenergymagic.org>
This commit is contained in:
parent
d43faa89f1
commit
451ef35011
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user