about summary refs log tree commit diff
path: root/tvix/eval/src/value/string.rs
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-08-25T11·23+0300
committertazjin <tazjin@tvl.su>2022-09-03T00·47+0000
commit39b01c302937fe3e91c15758b520c3ecc5379c7b (patch)
treecff95e288671533056fcea8a62e3fe620b77ef6d /tvix/eval/src/value/string.rs
parent265393301e2b59659a0a5db15e42bc668d0c7c68 (diff)
fix(tvix/eval): correctly escape `${` in strings r/4607
Without this escape, it is possible for Nix to produce escaped
representations which are not literal Nix values again.

This was fixed in upstream Nix in
https://github.com/NixOS/nix/pull/4012 (though only for eval, not in
the REPL) and the updated test is picked from upstream after that commit.

Because we run the C++ Nix tests against our test suite as well, this
also bumps our custom Nix 2.3 to a commit that includes the
cherry-picked fix from the PR above.

Change-Id: I478547ade65f655c606ec46f7143932064192283
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6271
Reviewed-by: grfn <grfn@gws.fyi>
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
Diffstat (limited to 'tvix/eval/src/value/string.rs')
-rw-r--r--tvix/eval/src/value/string.rs26
1 files changed, 15 insertions, 11 deletions
diff --git a/tvix/eval/src/value/string.rs b/tvix/eval/src/value/string.rs
index f352281be3..51b0f03454 100644
--- a/tvix/eval/src/value/string.rs
+++ b/tvix/eval/src/value/string.rs
@@ -90,13 +90,14 @@ impl NixString {
     }
 }
 
-fn nix_escape_char(ch: char) -> Option<&'static str> {
-    match ch {
-        '\\' => Some("\\\\"),
-        '"' => Some("\\\""),
-        '\n' => Some("\\n"),
-        '\t' => Some("\\t"),
-        '\r' => Some("\\r"),
+fn nix_escape_char(ch: char, next: Option<&char>) -> Option<&'static str> {
+    match (ch, next) {
+        ('\\', _) => Some("\\\\"),
+        ('"', _) => Some("\\\""),
+        ('\n', _) => Some("\\n"),
+        ('\t', _) => Some("\\t"),
+        ('\r', _) => Some("\\r"),
+        ('$', Some('{')) => Some("\\$"),
         _ => None,
     }
 }
@@ -106,14 +107,17 @@ fn nix_escape_char(ch: char) -> Option<&'static str> {
 //
 // Note that this does not add the outer pair of surrounding quotes.
 fn nix_escape_string(input: &str) -> Cow<str> {
-    for (i, c) in input.chars().enumerate() {
-        if let Some(esc) = nix_escape_char(c) {
+    let mut iter = input.chars().enumerate().peekable();
+
+    while let Some((i, c)) = iter.next() {
+        if let Some(esc) = nix_escape_char(c, iter.peek().map(|(_, c)| c)) {
             let mut escaped = String::with_capacity(input.len());
             escaped.push_str(&input[..i]);
             escaped.push_str(esc);
 
-            for c in input[i + 1..].chars() {
-                match nix_escape_char(c) {
+            let mut inner_iter = input[i + 1..].chars().peekable();
+            while let Some(c) = inner_iter.next() {
+                match nix_escape_char(c, inner_iter.peek()) {
                     Some(esc) => escaped.push_str(esc),
                     None => escaped.push(c),
                 }