Repository: ruby/prism
Pull request #2630 (2024-03-25 17:23:16)

Frozen parts
Fixes https://github.com/ruby/prism/issues/2532

* Mark inner string parts of interpolated* nodes as frozen
* Mark interpolated* nodes as static literal if all of their parts are static literal


Patch:
diff --git a/src/prism.c b/src/prism.c
index cca0e028..23fc59fc 100644
--- a/src/prism.c
+++ b/src/prism.c
@@ -4281,6 +4281,7 @@ pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_tok
     *node = (pm_interpolated_regular_expression_node_t) {
         {
             .type = PM_INTERPOLATED_REGULAR_EXPRESSION_NODE,
+            .flags = PM_NODE_FLAG_STATIC_LITERAL,
             .location = {
                 .start = opening->start,
                 .end = NULL,
@@ -4302,6 +4303,15 @@ pm_interpolated_regular_expression_node_append(pm_interpolated_regular_expressio
     if (node->base.location.end < part->location.end) {
         node->base.location.end = part->location.end;
     }
+
+    if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
+        pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
+    }
+
+    if (!PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
+        pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL);
+    }
+
     pm_node_list_append(&node->parts, part);
 }
 
@@ -4322,6 +4332,7 @@ pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *openin
     *node = (pm_interpolated_string_node_t) {
         {
             .type = PM_INTERPOLATED_STRING_NODE,
+            .flags = PM_NODE_FLAG_STATIC_LITERAL,
             .location = {
                 .start = opening->start,
                 .end = closing->end,
@@ -4348,6 +4359,14 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_
         node->base.location.start = part->location.start;
     }
 
+    if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
+        pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
+    }
+
+    if (!PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
+        pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL);
+    }
+
     pm_node_list_append(&node->parts, part);
     node->base.location.end = part->location.end;
 }
@@ -4371,6 +4390,7 @@ pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *openin
     *node = (pm_interpolated_symbol_node_t) {
         {
             .type = PM_INTERPOLATED_SYMBOL_NODE,
+            .flags = PM_NODE_FLAG_STATIC_LITERAL,
             .location = {
                 .start = opening->start,
                 .end = closing->end,
@@ -4394,6 +4414,14 @@ pm_interpolated_symbol_node_append(pm_interpolated_symbol_node_t *node, pm_node_
         node->base.location.start = part->location.start;
     }
 
+    if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
+        pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
+    }
+
+    if (!PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
+        pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL);
+    }
+
     pm_node_list_append(&node->parts, part);
     node->base.location.end = part->location.end;
 }
@@ -4408,6 +4436,7 @@ pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *openi
     *node = (pm_interpolated_x_string_node_t) {
         {
             .type = PM_INTERPOLATED_X_STRING_NODE,
+            .flags = PM_NODE_FLAG_STATIC_LITERAL,
             .location = {
                 .start = opening->start,
                 .end = closing->end
@@ -4423,6 +4452,14 @@ pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *openi
 
 static inline void
 pm_interpolated_xstring_node_append(pm_interpolated_x_string_node_t *node, pm_node_t *part) {
+    if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
+        pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
+    }
+
+    if (!PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
+        pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL);
+    }
+
     pm_node_list_append(&node->parts, part);
     node->base.location.end = part->location.end;
 }


Test patch:
diff --git a/test/prism/snapshots/dos_endings.txt b/test/prism/snapshots/dos_endings.txt
index c5b962f2..5ac946b2 100644
--- a/test/prism/snapshots/dos_endings.txt
+++ b/test/prism/snapshots/dos_endings.txt
@@ -18,13 +18,13 @@
         │   │           ├── opening_loc: ∅
         │   │           ├── parts: (length: 2)
         │   │           │   ├── @ StringNode (location: (1,5)-(1,9))
-        │   │           │   │   ├── flags: ∅
+        │   │           │   │   ├── flags: frozen
         │   │           │   │   ├── opening_loc: (1,5)-(1,6) = "\""
         │   │           │   │   ├── content_loc: (1,6)-(1,8) = "hi"
         │   │           │   │   ├── closing_loc: (1,8)-(1,9) = "\""
         │   │           │   │   └── unescaped: "hi"
         │   │           │   └── @ StringNode (location: (2,5)-(2,12))
-        │   │           │       ├── flags: ∅
+        │   │           │       ├── flags: frozen
         │   │           │       ├── opening_loc: (2,5)-(2,6) = "\""
         │   │           │       ├── content_loc: (2,6)-(2,11) = "there"
         │   │           │       ├── closing_loc: (2,11)-(2,12) = "\""
diff --git a/test/prism/snapshots/dstring.txt b/test/prism/snapshots/dstring.txt
index ad395f8a..64ab6e8a 100644
--- a/test/prism/snapshots/dstring.txt
+++ b/test/prism/snapshots/dstring.txt
@@ -39,13 +39,13 @@
         │   ├── opening_loc: ∅
         │   ├── parts: (length: 2)
         │   │   ├── @ StringNode (location: (7,0)-(8,2))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: (7,0)-(7,1) = "\""
         │   │   │   ├── content_loc: (7,1)-(8,1) = "fo\no"
         │   │   │   ├── closing_loc: (8,1)-(8,2) = "\""
         │   │   │   └── unescaped: "fo\no"
         │   │   └── @ StringNode (location: (8,3)-(9,2))
-        │   │       ├── flags: ∅
+        │   │       ├── flags: frozen
         │   │       ├── opening_loc: (8,3)-(8,4) = "\""
         │   │       ├── content_loc: (8,4)-(9,1) = "ba\nr"
         │   │       ├── closing_loc: (9,1)-(9,2) = "\""
diff --git a/test/prism/snapshots/regex.txt b/test/prism/snapshots/regex.txt
index 9e19bbb1..d07ab8c5 100644
--- a/test/prism/snapshots/regex.txt
+++ b/test/prism/snapshots/regex.txt
@@ -39,7 +39,7 @@
         │   ├── opening_loc: (7,0)-(7,1) = "/"
         │   ├── parts: (length: 2)
         │   │   ├── @ StringNode (location: (7,1)-(7,5))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: ∅
         │   │   │   ├── content_loc: (7,1)-(7,5) = "aaa "
         │   │   │   ├── closing_loc: ∅
@@ -55,7 +55,7 @@
         │   ├── opening_loc: (9,0)-(9,1) = "/"
         │   ├── parts: (length: 3)
         │   │   ├── @ StringNode (location: (9,1)-(9,5))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: ∅
         │   │   │   ├── content_loc: (9,1)-(9,5) = "aaa "
         │   │   │   ├── closing_loc: ∅
@@ -77,7 +77,7 @@
         │   │   │   │           └── block: ∅
         │   │   │   └── closing_loc: (9,10)-(9,11) = "}"
         │   │   └── @ StringNode (location: (9,11)-(9,15))
-        │   │       ├── flags: ∅
+        │   │       ├── flags: frozen
         │   │       ├── opening_loc: ∅
         │   │       ├── content_loc: (9,11)-(9,15) = " ccc"
         │   │       ├── closing_loc: ∅
@@ -192,7 +192,7 @@
         │   ├── opening_loc: (30,0)-(30,1) = "/"
         │   ├── parts: (length: 2)
         │   │   ├── @ StringNode (location: (30,1)-(30,5))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: ∅
         │   │   │   ├── content_loc: (30,1)-(30,5) = "aaa "
         │   │   │   ├── closing_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/parse_line_evstr_after_break.txt b/test/prism/snapshots/seattlerb/parse_line_evstr_after_break.txt
index 9cb200e9..d8acbc05 100644
--- a/test/prism/snapshots/seattlerb/parse_line_evstr_after_break.txt
+++ b/test/prism/snapshots/seattlerb/parse_line_evstr_after_break.txt
@@ -7,7 +7,7 @@
             ├── opening_loc: ∅
             ├── parts: (length: 2)
             │   ├── @ StringNode (location: (1,0)-(1,3))
-            │   │   ├── flags: ∅
+            │   │   ├── flags: frozen
             │   │   ├── opening_loc: (1,0)-(1,1) = "\""
             │   │   ├── content_loc: (1,1)-(1,2) = "a"
             │   │   ├── closing_loc: (1,2)-(1,3) = "\""
diff --git a/test/prism/snapshots/seattlerb/qsymbols_interp.txt b/test/prism/snapshots/seattlerb/qsymbols_interp.txt
index 23c39302..97bc6754 100644
--- a/test/prism/snapshots/seattlerb/qsymbols_interp.txt
+++ b/test/prism/snapshots/seattlerb/qsymbols_interp.txt
@@ -16,7 +16,7 @@
             │   │   ├── opening_loc: ∅
             │   │   ├── parts: (length: 2)
             │   │   │   ├── @ StringNode (location: (1,5)-(1,6))
-            │   │   │   │   ├── flags: ∅
+            │   │   │   │   ├── flags: frozen
             │   │   │   │   ├── opening_loc: ∅
             │   │   │   │   ├── content_loc: (1,5)-(1,6) = "b"
             │   │   │   │   ├── closing_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/str_lit_concat_bad_encodings.txt b/test/prism/snapshots/seattlerb/str_lit_concat_bad_encodings.txt
index b841407c..b31245a7 100644
--- a/test/prism/snapshots/seattlerb/str_lit_concat_bad_encodings.txt
+++ b/test/prism/snapshots/seattlerb/str_lit_concat_bad_encodings.txt
@@ -7,13 +7,13 @@
             ├── opening_loc: ∅
             ├── parts: (length: 2)
             │   ├── @ StringNode (location: (1,0)-(1,62))
-            │   │   ├── flags: forced_utf8_encoding
+            │   │   ├── flags: forced_utf8_encoding, frozen
             │   │   ├── opening_loc: (1,0)-(1,1) = "\""
             │   │   ├── content_loc: (1,1)-(1,61) = "\\xE3\\xD3\\x8B\\xE3\\x83\\xBC\\x83\\xE3\\x83\\xE3\\x82\\xB3\\xA3\\x82\\x99"
             │   │   ├── closing_loc: (1,61)-(1,62) = "\""
             │   │   └── unescaped: "\xE3Ӌー\x83\xE3\x83コ\xA3\x82\x99"
             │   └── @ StringNode (location: (2,8)-(2,66))
-            │       ├── flags: forced_utf8_encoding
+            │       ├── flags: forced_utf8_encoding, frozen
             │       ├── opening_loc: (2,8)-(2,9) = "\""
             │       ├── content_loc: (2,9)-(2,65) = "\\xE3\\x83\\xB3\\xE3\\x83\\x8F\\xE3\\x82\\x9A\\xC3\\xBD;foo@bar.com"
             │       ├── closing_loc: (2,65)-(2,66) = "\""
diff --git a/test/prism/snapshots/seattlerb/words_interp.txt b/test/prism/snapshots/seattlerb/words_interp.txt
index dfead7d3..e0b08bc0 100644
--- a/test/prism/snapshots/seattlerb/words_interp.txt
+++ b/test/prism/snapshots/seattlerb/words_interp.txt
@@ -19,7 +19,7 @@
             │       │   │   │           └── value: 1
             │       │   │   └── closing_loc: (1,6)-(1,7) = "}"
             │       │   └── @ StringNode (location: (1,7)-(1,8))
-            │       │       ├── flags: ∅
+            │       │       ├── flags: frozen
             │       │       ├── opening_loc: ∅
             │       │       ├── content_loc: (1,7)-(1,8) = "b"
             │       │       ├── closing_loc: ∅
diff --git a/test/prism/snapshots/spanning_heredoc.txt b/test/prism/snapshots/spanning_heredoc.txt
index 90297d22..7c3db913 100644
--- a/test/prism/snapshots/spanning_heredoc.txt
+++ b/test/prism/snapshots/spanning_heredoc.txt
@@ -36,13 +36,13 @@
         │   │           │       │   ├── opening_loc: (4,13)-(4,14) = "/"
         │   │           │       │   ├── parts: (length: 2)
         │   │           │       │   │   ├── @ StringNode (location: (4,14)-(4,16))
-        │   │           │       │   │   │   ├── flags: ∅
+        │   │           │       │   │   │   ├── flags: frozen
         │   │           │       │   │   │   ├── opening_loc: ∅
         │   │           │       │   │   │   ├── content_loc: (4,14)-(4,16) = "b\\"
         │   │           │       │   │   │   ├── closing_loc: ∅
         │   │           │       │   │   │   └── unescaped: "b"
         │   │           │       │   │   └── @ StringNode (location: (7,0)-(7,1))
-        │   │           │       │   │       ├── flags: ∅
+        │   │           │       │   │       ├── flags: frozen
         │   │           │       │   │       ├── opening_loc: ∅
         │   │           │       │   │       ├── content_loc: (7,0)-(7,1) = "b"
         │   │           │       │   │       ├── closing_loc: ∅
@@ -223,13 +223,13 @@
         │   │           │       ├── opening_loc: ∅
         │   │           │       ├── parts: (length: 2)
         │   │           │       │   ├── @ StringNode (location: (35,12)-(35,14))
-        │   │           │       │   │   ├── flags: ∅
+        │   │           │       │   │   ├── flags: frozen
         │   │           │       │   │   ├── opening_loc: ∅
         │   │           │       │   │   ├── content_loc: (35,12)-(35,14) = "l\\"
         │   │           │       │   │   ├── closing_loc: ∅
         │   │           │       │   │   └── unescaped: "l\n"
         │   │           │       │   └── @ StringNode (location: (38,0)-(38,1))
-        │   │           │       │       ├── flags: ∅
+        │   │           │       │       ├── flags: frozen
         │   │           │       │       ├── opening_loc: ∅
         │   │           │       │       ├── content_loc: (38,0)-(38,1) = "l"
         │   │           │       │       ├── closing_loc: ∅
@@ -299,13 +299,13 @@
         │   │           │       ├── opening_loc: ∅
         │   │           │       ├── parts: (length: 2)
         │   │           │       │   ├── @ StringNode (location: (48,12)-(48,14))
-        │   │           │       │   │   ├── flags: ∅
+        │   │           │       │   │   ├── flags: frozen
         │   │           │       │   │   ├── opening_loc: ∅
         │   │           │       │   │   ├── content_loc: (48,12)-(48,14) = "p\\"
         │   │           │       │   │   ├── closing_loc: ∅
         │   │           │       │   │   └── unescaped: "p\n"
         │   │           │       │   └── @ StringNode (location: (48,12)-(48,14))
-        │   │           │       │       ├── flags: ∅
+        │   │           │       │       ├── flags: frozen
         │   │           │       │       ├── opening_loc: ∅
         │   │           │       │       ├── content_loc: (48,12)-(48,14) = "p\\"
         │   │           │       │       ├── closing_loc: ∅
@@ -331,13 +331,13 @@
         │   │   │   ├── opening_loc: (53,5)-(53,6) = "/"
         │   │   │   ├── parts: (length: 2)
         │   │   │   │   ├── @ StringNode (location: (53,6)-(53,7))
-        │   │   │   │   │   ├── flags: ∅
+        │   │   │   │   │   ├── flags: frozen
         │   │   │   │   │   ├── opening_loc: ∅
         │   │   │   │   │   ├── content_loc: (53,6)-(53,7) = "\\"
         │   │   │   │   │   ├── closing_loc: ∅
         │   │   │   │   │   └── unescaped: ""
         │   │   │   │   └── @ StringNode (location: (55,0)-(55,6))
-        │   │   │   │       ├── flags: ∅
+        │   │   │   │       ├── flags: frozen
         │   │   │   │       ├── opening_loc: ∅
         │   │   │   │       ├── content_loc: (55,0)-(55,6) = "(?<a>)"
         │   │   │   │       ├── closing_loc: ∅
diff --git a/test/prism/snapshots/strings.txt b/test/prism/snapshots/strings.txt
index 40a05ce0..471b3713 100644
--- a/test/prism/snapshots/strings.txt
+++ b/test/prism/snapshots/strings.txt
@@ -337,7 +337,7 @@
         │   │   │   ├── opening_loc: ∅
         │   │   │   ├── parts: (length: 3)
         │   │   │   │   ├── @ StringNode (location: (67,5)-(67,6))
-        │   │   │   │   │   ├── flags: ∅
+        │   │   │   │   │   ├── flags: frozen
         │   │   │   │   │   ├── opening_loc: ∅
         │   │   │   │   │   ├── content_loc: (67,5)-(67,6) = "b"
         │   │   │   │   │   ├── closing_loc: ∅
@@ -359,7 +359,7 @@
         │   │   │   │   │   │           └── block: ∅
         │   │   │   │   │   └── closing_loc: (67,9)-(67,10) = "}"
         │   │   │   │   └── @ StringNode (location: (67,10)-(67,11))
-        │   │   │   │       ├── flags: ∅
+        │   │   │   │       ├── flags: frozen
         │   │   │   │       ├── opening_loc: ∅
         │   │   │   │       ├── content_loc: (67,10)-(67,11) = "d"
         │   │   │   │       ├── closing_loc: ∅
@@ -495,13 +495,13 @@
         │   ├── opening_loc: ∅
         │   ├── parts: (length: 2)
         │   │   ├── @ StringNode (location: (99,0)-(99,2))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: (99,0)-(99,1) = "?"
         │   │   │   ├── content_loc: (99,1)-(99,2) = "a"
         │   │   │   ├── closing_loc: ∅
         │   │   │   └── unescaped: "a"
         │   │   └── @ StringNode (location: (99,3)-(99,6))
-        │   │       ├── flags: ∅
+        │   │       ├── flags: frozen
         │   │       ├── opening_loc: (99,3)-(99,4) = "\""
         │   │       ├── content_loc: (99,4)-(99,5) = "a"
         │   │       ├── closing_loc: (99,5)-(99,6) = "\""
diff --git a/test/prism/snapshots/symbols.txt b/test/prism/snapshots/symbols.txt
index c34be74b..b6a854db 100644
--- a/test/prism/snapshots/symbols.txt
+++ b/test/prism/snapshots/symbols.txt
@@ -234,7 +234,7 @@
         │   │   │   ├── opening_loc: ∅
         │   │   │   ├── parts: (length: 2)
         │   │   │   │   ├── @ StringNode (location: (39,5)-(39,6))
-        │   │   │   │   │   ├── flags: ∅
+        │   │   │   │   │   ├── flags: frozen
         │   │   │   │   │   ├── opening_loc: ∅
         │   │   │   │   │   ├── content_loc: (39,5)-(39,6) = "b"
         │   │   │   │   │   ├── closing_loc: ∅
@@ -262,7 +262,7 @@
         │   │   │   │   │   │           └── value: 2
         │   │   │   │   │   └── closing_loc: (39,14)-(39,15) = "}"
         │   │   │   │   └── @ StringNode (location: (39,15)-(39,16))
-        │   │   │   │       ├── flags: ∅
+        │   │   │   │       ├── flags: frozen
         │   │   │   │       ├── opening_loc: ∅
         │   │   │   │       ├── content_loc: (39,15)-(39,16) = "c"
         │   │   │   │       ├── closing_loc: ∅
@@ -272,7 +272,7 @@
         │   │       ├── opening_loc: ∅
         │   │       ├── parts: (length: 3)
         │   │       │   ├── @ StringNode (location: (39,17)-(39,18))
-        │   │       │   │   ├── flags: ∅
+        │   │       │   │   ├── flags: frozen
         │   │       │   │   ├── opening_loc: ∅
         │   │       │   │   ├── content_loc: (39,17)-(39,18) = "d"
         │   │       │   │   ├── closing_loc: ∅
@@ -287,7 +287,7 @@
         │   │       │   │   │           └── value: 3
         │   │       │   │   └── closing_loc: (39,21)-(39,22) = "}"
         │   │       │   └── @ StringNode (location: (39,22)-(39,23))
-        │   │       │       ├── flags: ∅
+        │   │       │       ├── flags: frozen
         │   │       │       ├── opening_loc: ∅
         │   │       │       ├── content_loc: (39,22)-(39,23) = "f"
         │   │       │       ├── closing_loc: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/literal.txt b/test/prism/snapshots/unparser/corpus/literal/literal.txt
index ba7dd70b..8ecf613e 100644
--- a/test/prism/snapshots/unparser/corpus/literal/literal.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/literal.txt
@@ -358,13 +358,13 @@
         │   ├── opening_loc: ∅
         │   ├── parts: (length: 2)
         │   │   ├── @ StringNode (location: (28,0)-(28,5))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: (28,0)-(28,1) = "\""
         │   │   │   ├── content_loc: (28,1)-(28,4) = "foo"
         │   │   │   ├── closing_loc: (28,4)-(28,5) = "\""
         │   │   │   └── unescaped: "foo"
         │   │   └── @ StringNode (location: (28,6)-(28,11))
-        │   │       ├── flags: ∅
+        │   │       ├── flags: frozen
         │   │       ├── opening_loc: (28,6)-(28,7) = "\""
         │   │       ├── content_loc: (28,7)-(28,10) = "bar"
         │   │       ├── closing_loc: (28,10)-(28,11) = "\""
@@ -497,7 +497,7 @@
         │   ├── opening_loc: (39,0)-(39,1) = "`"
         │   ├── parts: (length: 2)
         │   │   ├── @ StringNode (location: (39,1)-(39,4))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: ∅
         │   │   │   ├── content_loc: (39,1)-(39,4) = "foo"
         │   │   │   ├── closing_loc: ∅
@@ -582,7 +582,7 @@
         │   ├── opening_loc: (51,0)-(51,1) = "/"
         │   ├── parts: (length: 2)
         │   │   ├── @ StringNode (location: (51,1)-(51,4))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: ∅
         │   │   │   ├── content_loc: (51,1)-(51,4) = "foo"
         │   │   │   ├── closing_loc: ∅
@@ -601,7 +601,7 @@
         │   ├── opening_loc: (52,0)-(52,1) = "/"
         │   ├── parts: (length: 2)
         │   │   ├── @ StringNode (location: (52,1)-(52,4))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: ∅
         │   │   │   ├── content_loc: (52,1)-(52,4) = "foo"
         │   │   │   ├── closing_loc: ∅
@@ -1156,7 +1156,7 @@
             ├── opening_loc: (89,0)-(89,1) = "`"
             ├── parts: (length: 3)
             │   ├── @ StringNode (location: (89,1)-(90,0))
-            │   │   ├── flags: ∅
+            │   │   ├── flags: frozen
             │   │   ├── opening_loc: ∅
             │   │   ├── content_loc: (89,1)-(90,0) = "  x\n"
             │   │   ├── closing_loc: ∅
@@ -1178,7 +1178,7 @@
             │   │   │           └── block: ∅
             │   │   └── closing_loc: (90,5)-(90,6) = "}"
             │   └── @ StringNode (location: (90,6)-(91,1))
-            │       ├── flags: ∅
+            │       ├── flags: frozen
             │       ├── opening_loc: ∅
             │       ├── content_loc: (90,6)-(91,1) = "\n#"
             │       ├── closing_loc: ∅
diff --git a/test/prism/snapshots/unparser/corpus/semantic/dstr.txt b/test/prism/snapshots/unparser/corpus/semantic/dstr.txt
index 5ab954b6..c5337729 100644
--- a/test/prism/snapshots/unparser/corpus/semantic/dstr.txt
+++ b/test/prism/snapshots/unparser/corpus/semantic/dstr.txt
@@ -429,7 +429,7 @@
         │   ├── opening_loc: ∅
         │   ├── parts: (length: 2)
         │   │   ├── @ StringNode (location: (119,0)-(119,3))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: (119,0)-(119,1) = "'"
         │   │   │   ├── content_loc: (119,1)-(119,2) = "a"
         │   │   │   ├── closing_loc: (119,2)-(119,3) = "'"
@@ -447,19 +447,19 @@
         │   ├── opening_loc: ∅
         │   ├── parts: (length: 3)
         │   │   ├── @ StringNode (location: (122,0)-(122,2))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: (122,0)-(122,1) = "\""
         │   │   │   ├── content_loc: (122,1)-(122,1) = ""
         │   │   │   ├── closing_loc: (122,1)-(122,2) = "\""
         │   │   │   └── unescaped: ""
         │   │   ├── @ StringNode (location: (122,3)-(122,5))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: (122,3)-(122,4) = "\""
         │   │   │   ├── content_loc: (122,4)-(122,4) = ""
         │   │   │   ├── closing_loc: (122,4)-(122,5) = "\""
         │   │   │   └── unescaped: ""
         │   │   └── @ StringNode (location: (122,6)-(122,8))
-        │   │       ├── flags: ∅
+        │   │       ├── flags: frozen
         │   │       ├── opening_loc: (122,6)-(122,7) = "\""
         │   │       ├── content_loc: (122,7)-(122,7) = ""
         │   │       ├── closing_loc: (122,7)-(122,8) = "\""
@@ -487,7 +487,7 @@
         │   │   │   │       └── closing_loc: (124,6)-(124,7) = "}"
         │   │   │   └── closing_loc: (124,7)-(124,8) = "\""
         │   │   └── @ StringNode (location: (124,9)-(124,12))
-        │   │       ├── flags: ∅
+        │   │       ├── flags: frozen
         │   │       ├── opening_loc: (124,9)-(124,10) = "\""
         │   │       ├── content_loc: (124,10)-(124,11) = "b"
         │   │       ├── closing_loc: (124,11)-(124,12) = "\""
@@ -512,7 +512,7 @@
         │   │   │   │           └── name: :@a
         │   │   │   └── closing_loc: (125,5)-(125,6) = "\""
         │   │   └── @ StringNode (location: (125,7)-(125,10))
-        │   │       ├── flags: ∅
+        │   │       ├── flags: frozen
         │   │       ├── opening_loc: (125,7)-(125,8) = "\""
         │   │       ├── content_loc: (125,8)-(125,9) = "b"
         │   │       ├── closing_loc: (125,9)-(125,10) = "\""
@@ -537,7 +537,7 @@
         │   │   │   │           └── name: :$a
         │   │   │   └── closing_loc: (126,5)-(126,6) = "\""
         │   │   └── @ StringNode (location: (126,7)-(126,10))
-        │   │       ├── flags: ∅
+        │   │       ├── flags: frozen
         │   │       ├── opening_loc: (126,7)-(126,8) = "\""
         │   │       ├── content_loc: (126,8)-(126,9) = "b"
         │   │       ├── closing_loc: (126,9)-(126,10) = "\""
@@ -562,7 +562,7 @@
             │   │   │           └── name: :@@a
             │   │   └── closing_loc: (127,6)-(127,7) = "\""
             │   └── @ StringNode (location: (127,8)-(127,11))
-            │       ├── flags: ∅
+            │       ├── flags: frozen
             │       ├── opening_loc: (127,8)-(127,9) = "\""
             │       ├── content_loc: (127,9)-(127,10) = "b"
             │       ├── closing_loc: (127,10)-(127,11) = "\""
diff --git a/test/prism/snapshots/unparser/corpus/semantic/literal.txt b/test/prism/snapshots/unparser/corpus/semantic/literal.txt
index 59e02be6..ef666890 100644
--- a/test/prism/snapshots/unparser/corpus/semantic/literal.txt
+++ b/test/prism/snapshots/unparser/corpus/semantic/literal.txt
@@ -55,7 +55,7 @@
         │   │   │   │           └── name: :@bar
         │   │   │   └── closing_loc: (11,9)-(11,10) = "}"
         │   │   └── @ StringNode (location: (11,10)-(11,13))
-        │   │       ├── flags: ∅
+        │   │       ├── flags: frozen
         │   │       ├── opening_loc: ∅
         │   │       ├── content_loc: (11,10)-(11,13) = "baz"
         │   │       ├── closing_loc: ∅
diff --git a/test/prism/snapshots/whitequark/array_symbols_interp.txt b/test/prism/snapshots/whitequark/array_symbols_interp.txt
index 7583ad88..2437486b 100644
--- a/test/prism/snapshots/whitequark/array_symbols_interp.txt
+++ b/test/prism/snapshots/whitequark/array_symbols_interp.txt
@@ -41,7 +41,7 @@
             │       ├── opening_loc: ∅
             │       ├── parts: (length: 2)
             │       │   ├── @ StringNode (location: (3,3)-(3,6))
-            │       │   │   ├── flags: ∅
+            │       │   │   ├── flags: frozen
             │       │   │   ├── opening_loc: ∅
             │       │   │   ├── content_loc: (3,3)-(3,6) = "foo"
             │       │   │   ├── closing_loc: ∅
diff --git a/test/prism/snapshots/whitequark/array_words_interp.txt b/test/prism/snapshots/whitequark/array_words_interp.txt
index c2f23d2b..00fd05e7 100644
--- a/test/prism/snapshots/whitequark/array_words_interp.txt
+++ b/test/prism/snapshots/whitequark/array_words_interp.txt
@@ -63,7 +63,7 @@
             │       │   │   │           └── block: ∅
             │       │   │   └── closing_loc: (3,12)-(3,13) = "}"
             │       │   ├── @ StringNode (location: (3,13)-(3,16))
-            │       │   │   ├── flags: ∅
+            │       │   │   ├── flags: frozen
             │       │   │   ├── opening_loc: ∅
             │       │   │   ├── content_loc: (3,13)-(3,16) = "foo"
             │       │   │   ├── closing_loc: ∅
diff --git a/test/prism/snapshots/whitequark/non_lvar_injecting_match.txt b/test/prism/snapshots/whitequark/non_lvar_injecting_match.txt
index 2df571e8..584e997d 100644
--- a/test/prism/snapshots/whitequark/non_lvar_injecting_match.txt
+++ b/test/prism/snapshots/whitequark/non_lvar_injecting_match.txt
@@ -20,7 +20,7 @@
             │   │   │   │           └── value: 1
             │   │   │   └── closing_loc: (1,4)-(1,5) = "}"
             │   │   └── @ StringNode (location: (1,5)-(1,18))
-            │   │       ├── flags: ∅
+            │   │       ├── flags: frozen
             │   │       ├── opening_loc: ∅
             │   │       ├── content_loc: (1,5)-(1,18) = "(?<match>bar)"
             │   │       ├── closing_loc: ∅
diff --git a/test/prism/snapshots/whitequark/regex_interp.txt b/test/prism/snapshots/whitequark/regex_interp.txt
index ee8caf22..0a6db4cf 100644
--- a/test/prism/snapshots/whitequark/regex_interp.txt
+++ b/test/prism/snapshots/whitequark/regex_interp.txt
@@ -8,7 +8,7 @@
             ├── opening_loc: (1,0)-(1,1) = "/"
             ├── parts: (length: 3)
             │   ├── @ StringNode (location: (1,1)-(1,4))
-            │   │   ├── flags: ∅
+            │   │   ├── flags: frozen
             │   │   ├── opening_loc: ∅
             │   │   ├── content_loc: (1,1)-(1,4) = "foo"
             │   │   ├── closing_loc: ∅
@@ -30,7 +30,7 @@
             │   │   │           └── block: ∅
             │   │   └── closing_loc: (1,9)-(1,10) = "}"
             │   └── @ StringNode (location: (1,10)-(1,13))
-            │       ├── flags: ∅
+            │       ├── flags: frozen
             │       ├── opening_loc: ∅
             │       ├── content_loc: (1,10)-(1,13) = "baz"
             │       ├── closing_loc: ∅
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11990.txt b/test/prism/snapshots/whitequark/ruby_bug_11990.txt
index 43c04e55..abdb8d9d 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_11990.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_11990.txt
@@ -18,13 +18,13 @@
             │           ├── opening_loc: ∅
             │           ├── parts: (length: 2)
             │           │   ├── @ StringNode (location: (1,2)-(1,6))
-            │           │   │   ├── flags: ∅
+            │           │   │   ├── flags: frozen
             │           │   │   ├── opening_loc: (1,2)-(1,6) = "<<~E"
             │           │   │   ├── content_loc: (2,0)-(3,0) = "  x\n"
             │           │   │   ├── closing_loc: (3,0)-(4,0) = "E\n"
             │           │   │   └── unescaped: "x\n"
             │           │   └── @ StringNode (location: (1,7)-(1,12))
-            │           │       ├── flags: ∅
+            │           │       ├── flags: frozen
             │           │       ├── opening_loc: (1,7)-(1,8) = "\""
             │           │       ├── content_loc: (1,8)-(1,11) = "  y"
             │           │       ├── closing_loc: (1,11)-(1,12) = "\""
diff --git a/test/prism/snapshots/whitequark/string_concat.txt b/test/prism/snapshots/whitequark/string_concat.txt
index 38ea0c74..1739b2f6 100644
--- a/test/prism/snapshots/whitequark/string_concat.txt
+++ b/test/prism/snapshots/whitequark/string_concat.txt
@@ -22,7 +22,7 @@
             │   │   │           └── name: :@a
             │   │   └── closing_loc: (1,7)-(1,8) = "\""
             │   └── @ StringNode (location: (1,9)-(1,14))
-            │       ├── flags: ∅
+            │       ├── flags: frozen
             │       ├── opening_loc: (1,9)-(1,10) = "\""
             │       ├── content_loc: (1,10)-(1,13) = "bar"
             │       ├── closing_loc: (1,13)-(1,14) = "\""
diff --git a/test/prism/snapshots/whitequark/xstring_interp.txt b/test/prism/snapshots/whitequark/xstring_interp.txt
index e9543098..0676ea96 100644
--- a/test/prism/snapshots/whitequark/xstring_interp.txt
+++ b/test/prism/snapshots/whitequark/xstring_interp.txt
@@ -7,7 +7,7 @@
             ├── opening_loc: (1,0)-(1,1) = "`"
             ├── parts: (length: 3)
             │   ├── @ StringNode (location: (1,1)-(1,4))
-            │   │   ├── flags: ∅
+            │   │   ├── flags: frozen
             │   │   ├── opening_loc: ∅
             │   │   ├── content_loc: (1,1)-(1,4) = "foo"
             │   │   ├── closing_loc: ∅
@@ -29,7 +29,7 @@
             │   │   │           └── block: ∅
             │   │   └── closing_loc: (1,9)-(1,10) = "}"
             │   └── @ StringNode (location: (1,10)-(1,13))
-            │       ├── flags: ∅
+            │       ├── flags: frozen
             │       ├── opening_loc: ∅
             │       ├── content_loc: (1,10)-(1,13) = "baz"
             │       ├── closing_loc: ∅
diff --git a/test/prism/snapshots/xstring.txt b/test/prism/snapshots/xstring.txt
index 04b4cbf6..1a177026 100644
--- a/test/prism/snapshots/xstring.txt
+++ b/test/prism/snapshots/xstring.txt
@@ -13,7 +13,7 @@
         │   ├── opening_loc: (3,0)-(3,1) = "`"
         │   ├── parts: (length: 3)
         │   │   ├── @ StringNode (location: (3,1)-(3,5))
-        │   │   │   ├── flags: ∅
+        │   │   │   ├── flags: frozen
         │   │   │   ├── opening_loc: ∅
         │   │   │   ├── content_loc: (3,1)-(3,5) = "foo "
         │   │   │   ├── closing_loc: ∅
@@ -35,7 +35,7 @@
         │   │   │   │           └── block: ∅
         │   │   │   └── closing_loc: (3,10)-(3,11) = "}"
         │   │   └── @ StringNode (location: (3,11)-(3,15))
-        │   │       ├── flags: ∅
+        │   │       ├── flags: frozen
         │   │       ├── opening_loc: ∅
         │   │       ├── content_loc: (3,11)-(3,15) = " baz"
         │   │       ├── closing_loc: ∅