about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAdam Joseph <adam@westernsemico.com>2022-10-25T08·56-0700
committerclbot <clbot@tvl.fyi>2022-11-04T01·42+0000
commit4ec43bed5e1f6077402301aeee125870f755267b (patch)
treeae865efb7c87bdd4705f65f278c4d98212e8d3b8
parentd8841376e733e53af234bd924d7841c34d9b0c61 (diff)
fix(tvix/eval): quote keys which are not valid identifiers r/5241
The impl Display for NixAttrs needs to wrap double quotes around any
keys which are not valid Nix identifiers.  This commit does that,
and adds a test (which fails prior to this commit and passes after
this commit).

Change-Id: Ie31ce91e8637cb27073f23f115db81feefdc6424
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7084
Autosubmit: Adam Joseph <adam@westernsemico.com>
Reviewed-by: tazjin <tazjin@tvl.su>
Tested-by: BuildkiteCI
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.nix1
-rw-r--r--tvix/eval/src/value/string.rs25
3 files changed, 26 insertions, 1 deletions
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.exp
new file mode 100644
index 0000000000..aa98a082a8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.exp
@@ -0,0 +1 @@
+{ "3" = 3; }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.nix
new file mode 100644
index 0000000000..aa98a082a8
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-escapify-integer-keys.nix
@@ -0,0 +1 @@
+{ "3" = 3; }
diff --git a/tvix/eval/src/value/string.rs b/tvix/eval/src/value/string.rs
index 4caef653f2..66697a7f2f 100644
--- a/tvix/eval/src/value/string.rs
+++ b/tvix/eval/src/value/string.rs
@@ -118,7 +118,13 @@ impl NixString {
         match escaped {
             // A borrowed string is unchanged and can be returned as
             // is.
-            Cow::Borrowed(_) => escaped,
+            Cow::Borrowed(_) => {
+                if is_valid_nix_identifier(&escaped) {
+                    escaped
+                } else {
+                    Cow::Owned(format!("\"{}\"", escaped))
+                }
+            }
 
             // An owned string has escapes, and needs the outer quotes
             // for display.
@@ -152,6 +158,23 @@ fn nix_escape_char(ch: char, next: Option<&char>) -> Option<&'static str> {
     }
 }
 
+/// Return true if this string can be used as an identifier in Nix.
+fn is_valid_nix_identifier(s: &str) -> bool {
+    // adapted from rnix-parser's tokenizer.rs
+    let mut chars = s.chars();
+    match chars.next() {
+        Some('a'..='z' | 'A'..='Z' | '_') => (),
+        _ => return false,
+    }
+    for c in chars {
+        match c {
+            'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' => (),
+            _ => return false,
+        }
+    }
+    return true;
+}
+
 /// Escape a Nix string for display, as most user-visible representation
 /// are escaped strings.
 ///