Repository: djc/askama
Pull request #615 (2022-01-31 07:40:15)

Make is_shadowing_variable() failable
Extracted from #612.


Patch:
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs
index 7080b87..acd716b 100644
--- a/askama_shared/src/generator.rs
+++ b/askama_shared/src/generator.rs
@@ -804,26 +804,38 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
         buf.writeln(";")
     }
 
-    fn is_shadowing_variable(&self, var: &Target<'a>) -> bool {
+    fn is_shadowing_variable(&self, var: &Target<'a>) -> Result<bool, CompileError> {
         match var {
             Target::Name(name) => {
                 let name = normalize_identifier(name);
                 match self.locals.get(&name) {
                     // declares a new variable
-                    None => false,
+                    None => Ok(false),
                     // an initialized variable gets shadowed
-                    Some(meta) if meta.initialized => true,
+                    Some(meta) if meta.initialized => Ok(true),
                     // initializes a variable that was introduced in a LetDecl before
-                    _ => false,
+                    _ => Ok(false),
                 }
             }
-            Target::Tuple(_, targets) => targets
-                .iter()
-                .any(|target| self.is_shadowing_variable(target)),
-            Target::Struct(_, named_targets) => named_targets
-                .iter()
-                .any(|(_, target)| self.is_shadowing_variable(target)),
-            _ => panic!("Cannot have literals on the left-hand-side of an assignment."),
+            Target::Tuple(_, targets) => {
+                for target in targets {
+                    match self.is_shadowing_variable(target) {
+                        Ok(false) => continue,
+                        outcome => return outcome,
+                    }
+                }
+                Ok(false)
+            }
+            Target::Struct(_, named_targets) => {
+                for (_, target) in named_targets {
+                    match self.is_shadowing_variable(target) {
+                        Ok(false) => continue,
+                        outcome => return outcome,
+                    }
+                }
+                Ok(false)
+            }
+            _ => Err("literals are not allowed on the left-hand side of an assignment".into()),
         }
     }
 
@@ -838,7 +850,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
         let mut expr_buf = Buffer::new(0);
         self.visit_expr(&mut expr_buf, val)?;
 
-        let shadowed = self.is_shadowing_variable(var);
+        let shadowed = self.is_shadowing_variable(var)?;
         if shadowed {
             // Need to flush the buffer if the variable is being shadowed,
             // to ensure the old variable is used.


Test patch:
diff --git a/testing/tests/ui/lit_on_assignment_lhs.stderr b/testing/tests/ui/lit_on_assignment_lhs.stderr
index fa488cb..53bfc81 100644
--- a/testing/tests/ui/lit_on_assignment_lhs.stderr
+++ b/testing/tests/ui/lit_on_assignment_lhs.stderr
@@ -1,7 +1,7 @@
-error: proc-macro derive panicked
- --> $DIR/lit_on_assignment_lhs.rs:3:10
+error: literals are not allowed on the left-hand side of an assignment
+ --> tests/ui/lit_on_assignment_lhs.rs:3:10
   |
 3 | #[derive(Template)]
   |          ^^^^^^^^
   |
-  = help: message: Cannot have literals on the left-hand-side of an assignment.
+  = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)