mirror of
https://github.com/askama-rs/askama.git
synced 2025-10-02 07:20:55 +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("{");
|
buf.write("{");
|
||||||
for (i, param) in params.iter().enumerate() {
|
for (i, param) in params.iter().enumerate() {
|
||||||
if let Some(MatchParameter::Name(p)) = param.1 {
|
if let Some(MatchParameter::Name(p)) = param.1 {
|
||||||
|
let p = normalize_identifier(p);
|
||||||
self.locals.insert_with_default(p);
|
self.locals.insert_with_default(p);
|
||||||
} else {
|
} else {
|
||||||
self.locals.insert_with_default(param.0);
|
self.locals.insert_with_default(param.0);
|
||||||
@ -822,12 +823,14 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
|
|||||||
buf.write("let ");
|
buf.write("let ");
|
||||||
match *var {
|
match *var {
|
||||||
Target::Name(name) => {
|
Target::Name(name) => {
|
||||||
|
let name = normalize_identifier(name);
|
||||||
self.locals.insert_with_default(name);
|
self.locals.insert_with_default(name);
|
||||||
buf.write(name);
|
buf.write(name);
|
||||||
}
|
}
|
||||||
Target::Tuple(ref targets) => {
|
Target::Tuple(ref targets) => {
|
||||||
buf.write("(");
|
buf.write("(");
|
||||||
for name in targets {
|
for name in targets {
|
||||||
|
let name = normalize_identifier(name);
|
||||||
self.locals.insert_with_default(name);
|
self.locals.insert_with_default(name);
|
||||||
buf.write(name);
|
buf.write(name);
|
||||||
buf.write(",");
|
buf.write(",");
|
||||||
@ -851,6 +854,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
|
|||||||
|
|
||||||
match *var {
|
match *var {
|
||||||
Target::Name(name) => {
|
Target::Name(name) => {
|
||||||
|
let name = normalize_identifier(name);
|
||||||
let meta = self.locals.get(&name).cloned();
|
let meta = self.locals.get(&name).cloned();
|
||||||
|
|
||||||
let shadowed = matches!(&meta, Some(meta) if meta.initialized);
|
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());
|
self.locals.insert(name, LocalMeta::initialized());
|
||||||
}
|
}
|
||||||
Target::Tuple(ref targets) => {
|
Target::Tuple(ref targets) => {
|
||||||
let shadowed = targets
|
let shadowed = targets.iter().any(|name| {
|
||||||
.iter()
|
let name = normalize_identifier(name);
|
||||||
.any(|name| matches!(self.locals.get(&name), Some(meta) if meta.initialized));
|
matches!(self.locals.get(&name), Some(meta) if meta.initialized)
|
||||||
|
});
|
||||||
if shadowed {
|
if shadowed {
|
||||||
// Need to flush the buffer if the variable is being shadowed,
|
// Need to flush the buffer if the variable is being shadowed,
|
||||||
// to ensure the old variable is used.
|
// to ensure the old variable is used.
|
||||||
@ -878,6 +883,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
|
|||||||
|
|
||||||
buf.write("let (");
|
buf.write("let (");
|
||||||
for name in targets {
|
for name in targets {
|
||||||
|
let name = normalize_identifier(name);
|
||||||
self.locals.insert(name, LocalMeta::initialized());
|
self.locals.insert(name, LocalMeta::initialized());
|
||||||
buf.write(name);
|
buf.write(name);
|
||||||
buf.write(",");
|
buf.write(",");
|
||||||
@ -1442,7 +1448,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
|
|||||||
return DisplayWrap::Unwrapped;
|
return DisplayWrap::Unwrapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.write(&self.locals.resolve_or_self(&s));
|
buf.write(normalize_identifier(&self.locals.resolve_or_self(&s)));
|
||||||
DisplayWrap::Unwrapped
|
DisplayWrap::Unwrapped
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1453,6 +1459,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
|
|||||||
args: &[Expr],
|
args: &[Expr],
|
||||||
) -> Result<DisplayWrap, CompileError> {
|
) -> Result<DisplayWrap, CompileError> {
|
||||||
buf.write("(");
|
buf.write("(");
|
||||||
|
let s = normalize_identifier(s);
|
||||||
if !self.locals.contains(&s) && s != "self" {
|
if !self.locals.contains(&s) && s != "self" {
|
||||||
buf.write("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) {
|
fn visit_target(&mut self, buf: &mut Buffer, target: &'a Target) {
|
||||||
match *target {
|
match *target {
|
||||||
Target::Name(name) => {
|
Target::Name(name) => {
|
||||||
|
let name = normalize_identifier(name);
|
||||||
self.locals.insert_with_default(name);
|
self.locals.insert_with_default(name);
|
||||||
buf.write(name);
|
buf.write(name);
|
||||||
}
|
}
|
||||||
Target::Tuple(ref targets) => {
|
Target::Tuple(ref targets) => {
|
||||||
buf.write("(");
|
buf.write("(");
|
||||||
for name in targets {
|
for name in targets {
|
||||||
|
let name = normalize_identifier(name);
|
||||||
self.locals.insert_with_default(name);
|
self.locals.insert_with_default(name);
|
||||||
buf.write(name);
|
buf.write(name);
|
||||||
buf.write(",");
|
buf.write(",");
|
||||||
@ -1689,6 +1698,7 @@ where
|
|||||||
|
|
||||||
impl MapChain<'_, &str, LocalMeta> {
|
impl MapChain<'_, &str, LocalMeta> {
|
||||||
fn resolve(&self, name: &str) -> Option<String> {
|
fn resolve(&self, name: &str) -> Option<String> {
|
||||||
|
let name = normalize_identifier(name);
|
||||||
self.get(&name).map(|meta| match &meta.refs {
|
self.get(&name).map(|meta| match &meta.refs {
|
||||||
Some(expr) => expr.clone(),
|
Some(expr) => expr.clone(),
|
||||||
None => name.to_string(),
|
None => name.to_string(),
|
||||||
@ -1696,6 +1706,7 @@ impl MapChain<'_, &str, LocalMeta> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_or_self(&self, name: &str) -> String {
|
fn resolve_or_self(&self, name: &str) -> String {
|
||||||
|
let name = normalize_identifier(name);
|
||||||
self.resolve(name)
|
self.resolve(name)
|
||||||
.unwrap_or_else(|| format!("self.{}", name))
|
.unwrap_or_else(|| format!("self.{}", name))
|
||||||
}
|
}
|
||||||
@ -1732,3 +1743,72 @@ enum Writable<'a> {
|
|||||||
Lit(&'a str),
|
Lit(&'a str),
|
||||||
Expr(&'a Expr<'a>),
|
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