about summary refs log tree commit diff
path: root/tvix/eval/src/compiler/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/eval/src/compiler/mod.rs')
-rw-r--r--tvix/eval/src/compiler/mod.rs61
1 files changed, 39 insertions, 22 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
index 06170c183929..7ee262cad008 100644
--- a/tvix/eval/src/compiler/mod.rs
+++ b/tvix/eval/src/compiler/mod.rs
@@ -1212,42 +1212,59 @@ impl Compiler<'_, '_> {
         self.errors.push(Error { kind, span })
     }
 
-    /// Convert a non-dynamic string expression to a string if possible,
-    /// or raise an error.
-    fn expr_str_to_string(&self, expr: ast::Str) -> EvalResult<String> {
-        if expr.normalized_parts().len() == 1 {
-            if let ast::InterpolPart::Literal(s) = expr.normalized_parts().pop().unwrap() {
-                return Ok(s);
-            }
+    /// Convert a non-dynamic string expression to a string if possible.
+    fn expr_static_str(&self, node: &ast::Str) -> Option<String> {
+        let mut parts = node.normalized_parts();
+
+        if parts.len() != 1 {
+            return None;
         }
 
-        return Err(Error {
-            kind: ErrorKind::DynamicKeyInLet(expr.syntax().clone()),
-            span: self.span_for(&expr),
-        });
+        if let Some(ast::InterpolPart::Literal(lit)) = parts.pop() {
+            return Some(lit);
+        }
+
+        None
     }
 
-    /// Convert a single identifier path fragment to a string if possible,
-    /// or raise an error about the node being dynamic.
-    fn attr_to_string(&self, node: ast::Attr) -> EvalResult<String> {
+    /// Convert the provided `ast::Attr` into a statically known
+    /// string if possible.
+    fn expr_static_attr_str(&self, node: &ast::Attr) -> Option<String> {
         match node {
-            ast::Attr::Ident(ident) => Ok(ident.ident_token().unwrap().text().into()),
-            ast::Attr::Str(s) => self.expr_str_to_string(s),
+            ast::Attr::Ident(ident) => Some(ident.ident_token().unwrap().text().into()),
+            ast::Attr::Str(s) => self.expr_static_str(&s),
 
             // The dynamic node type is just a wrapper. C++ Nix does not
             // care about the dynamic wrapper when determining whether the
             // node itself is dynamic, it depends solely on the expression
             // inside (i.e. `let ${"a"} = 1; in a` is valid).
             ast::Attr::Dynamic(ref dynamic) => match dynamic.expr().unwrap() {
-                ast::Expr::Str(s) => self.expr_str_to_string(s),
-                _ => Err(Error {
-                    kind: ErrorKind::DynamicKeyInLet(node.syntax().clone()),
-                    span: self.span_for(&node),
-                }),
+                ast::Expr::Str(s) => self.expr_static_str(&s),
+                _ => None,
             },
         }
     }
 
+    /// Construct the error returned when a dynamic attribute is found
+    /// in a `let`-expression.
+    fn dynamic_key_error<N>(&self, node: &N) -> Error
+    where
+        N: AstNode<Language = rnix::NixLanguage>,
+    {
+        Error {
+            kind: ErrorKind::DynamicKeyInLet(node.syntax().clone()),
+            span: self.span_for(node),
+        }
+    }
+
+    /// Convert a single identifier path fragment of a let binding to
+    /// a string if possible, or raise an error about the node being
+    /// dynamic.
+    fn binding_name(&self, node: ast::Attr) -> EvalResult<String> {
+        self.expr_static_attr_str(&node)
+            .ok_or_else(|| self.dynamic_key_error(&node))
+    }
+
     /// Normalises identifier fragments into a single string vector
     /// for `let`-expressions; fails if fragments requiring dynamic
     /// computation are encountered.
@@ -1255,7 +1272,7 @@ impl Compiler<'_, '_> {
         &self,
         path: I,
     ) -> EvalResult<Vec<String>> {
-        path.map(|node| self.attr_to_string(node)).collect()
+        path.map(|node| self.binding_name(node)).collect()
     }
 }