Use raw identifiers for identifiers which collide with Rust keywords (#476)

Co-authored-by: Daniel Arbuckle <djarb@highenergymagic.org>
This commit is contained in:
SciStarterOwner 2021-04-21 00:27:18 -07:00 committed by GitHub
parent d43faa89f1
commit 451ef35011
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -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
}
}