Repository: ruby/yarp
Pull request #1265 (2023-08-15 21:48:15)

Trim down rust bindings
Expose fewer things, and don't manually maintain `yp_string_t` structure.

cc @turboladen - `yp_string_t` actually changed since you opened the PR 😓 so it's simpler to handle now. I've dropped things that we're considering "internal" for now. Tagging you just so you can see it - no pressure to review unless you have time!


Patch:
diff --git a/rust/yarp-sys/build.rs b/rust/yarp-sys/build.rs
index 96e0da54..95149806 100644
--- a/rust/yarp-sys/build.rs
+++ b/rust/yarp-sys/build.rs
@@ -17,7 +17,7 @@ fn main() {
     let bindings = generate_bindings(&ruby_include_path);
 
     // Write the bindings to file.
-    write_bindngs(&bindings);
+    write_bindings(&bindings);
 }
 
 /// Gets the path to project files (`librubyparser*`) at `[root]/build/`.
@@ -29,7 +29,8 @@ fn ruby_build_path() -> PathBuf {
         .unwrap()
 }
 
-/// Gets the path to the header files that `bindgen` needs for doing code generation.
+/// Gets the path to the header files that `bindgen` needs for doing code
+/// generation.
 ///
 fn ruby_include_path() -> PathBuf {
     cargo_manifest_path()
@@ -42,9 +43,9 @@ fn cargo_manifest_path() -> PathBuf {
     PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap())
 }
 
-/// Uses `bindgen` to generate bindings to the C API. Update this to allow new types/functions/etc
-/// to be generated (it's allowlisted to only expose functions that'd make sense for public
-/// consumption).
+/// Uses `bindgen` to generate bindings to the C API. Update this to allow new
+/// types/functions/etc to be generated (it's allowlisted to only expose
+/// functions that'd make sense for public consumption).
 ///
 /// This method only generates code in memory here--it doesn't write it to file.
 ///
@@ -65,31 +66,19 @@ fn generate_bindings(ruby_include_path: &Path) -> bindgen::Bindings {
         .size_t_is_usize(true)
         .sort_semantically(true)
         // Structs
-        .allowlist_type(r#"^yp_\w+_node_t"#)
-        .allowlist_type("yp_buffer_t")
         .allowlist_type("yp_comment_t")
         .allowlist_type("yp_diagnostic_t")
         .allowlist_type("yp_encoding_changed_callback_t")
         .allowlist_type("yp_encoding_decode_callback_t")
-        .allowlist_type("yp_memsize_t")
         .allowlist_type("yp_list_t")
         .allowlist_type("yp_node_t")
         .allowlist_type("yp_node_type")
-        .allowlist_type("yp_parser_t")
         .allowlist_type("yp_pack_size")
-        // TODO: Commenting this because I can't figure out how to get bindgen to generate the
-        // inner-unions nicely. Hand-rolling this in src/lib.rs for now.
-        // .allowlist_type("yp_string_t")
-        .blocklist_type(r#"^yp_string_t\S*"#)
-        .allowlist_type("yp_string_list_t")
-        .allowlist_type("yp_token_type_t")
+        .allowlist_type("yp_parser_t")
+        .allowlist_type("yp_string_t")
+        .allowlist_type(r#"^yp_\w+_node_t"#)
         // Enums
         .rustified_non_exhaustive_enum("yp_comment_type_t")
-        .rustified_non_exhaustive_enum("yp_context_t")
-        .rustified_non_exhaustive_enum("yp_heredoc_indent_t")
-        .rustified_non_exhaustive_enum("yp_heredoc_quote_t")
-        .rustified_non_exhaustive_enum("yp_lex_mode_t")
-        .rustified_non_exhaustive_enum("yp_lex_state_t")
         .rustified_non_exhaustive_enum("yp_node_type")
         .rustified_non_exhaustive_enum("yp_pack_encoding")
         .rustified_non_exhaustive_enum("yp_pack_endian")
@@ -99,35 +88,20 @@ fn generate_bindings(ruby_include_path: &Path) -> bindgen::Bindings {
         .rustified_non_exhaustive_enum("yp_pack_size")
         .rustified_non_exhaustive_enum("yp_pack_type")
         .rustified_non_exhaustive_enum("yp_pack_variant")
-        .rustified_non_exhaustive_enum("yp_token_type")
-        .rustified_non_exhaustive_enum("yp_unescape_type_t")
         // Functions
-        .allowlist_function("yp_buffer_init")
-        .allowlist_function("yp_buffer_free")
-        .allowlist_function("yp_node_destroy")
         .allowlist_function("yp_list_empty_p")
         .allowlist_function("yp_list_free")
-        .allowlist_function("yp_list_init")
-        .allowlist_function("yp_node_memsize")
+        .allowlist_function("yp_node_destroy")
         .allowlist_function("yp_pack_parse")
         .allowlist_function("yp_parse")
-        .allowlist_function("yp_parse_serialize")
         .allowlist_function("yp_parser_free")
         .allowlist_function("yp_parser_init")
         .allowlist_function("yp_parser_register_encoding_changed_callback")
         .allowlist_function("yp_parser_register_encoding_decode_callback")
-        .allowlist_function("yp_prettyprint")
-        .allowlist_function("yp_regexp_named_capture_group_names")
-        .allowlist_function("yp_serialize")
         .allowlist_function("yp_size_to_native")
         .allowlist_function("yp_string_free")
         .allowlist_function("yp_string_length")
         .allowlist_function("yp_string_source")
-        .allowlist_function("yp_string_list_init")
-        .allowlist_function("yp_string_list_free")
-        .allowlist_function("yp_token_type_to_str")
-        .allowlist_function("yp_unescape_calculate_difference")
-        .allowlist_function("yp_unescape_manipulate_string")
         .allowlist_function("yp_version")
         // Vars
         .allowlist_var(r#"^yp_encoding\S+"#)
@@ -135,9 +109,9 @@ fn generate_bindings(ruby_include_path: &Path) -> bindgen::Bindings {
         .expect("Unable to generate yarp bindings")
 }
 
-/// Write the bindings to the `$OUT_DIR/bindings.rs` file. We'll pull these into the actual library
-/// in `src/lib.rs`.
-fn write_bindngs(bindings: &bindgen::Bindings) {
+/// Write the bindings to the `$OUT_DIR/bindings.rs` file. We'll pull these into
+/// the actual library in `src/lib.rs`.
+fn write_bindings(bindings: &bindgen::Bindings) {
     let out_path = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
 
     bindings
diff --git a/rust/yarp-sys/src/lib.rs b/rust/yarp-sys/src/lib.rs
index d0d0da82..9648a3a0 100644
--- a/rust/yarp-sys/src/lib.rs
+++ b/rust/yarp-sys/src/lib.rs
@@ -34,38 +34,10 @@
 #[allow(non_snake_case)]
 #[allow(missing_docs)]
 mod bindings {
-    use std::ffi::c_char;
-
-    // In `build.rs`, we use `bindgen` to generate bindings based on C headers and `librubyparser`. Here
-    // is where we pull in those bindings and make them part of our library.
+    // In `build.rs`, we use `bindgen` to generate bindings based on C headers
+    // and `librubyparser`. Here is where we pull in those bindings and make
+    // them part of our library.
     include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
-
-    pub type yp_parser_t_lex_modes = yp_parser__bindgen_ty_1;
-
-    //  ╭──────────────────────────────────────────────────────────────────────────────╮
-    //  │ Hand-rolling `yp_string_t` things                                            │
-    //  │                                                                              │
-    //  │ `bindgen` can generate these, but because of the nested `union`s, the        │
-    //  │ types end up with weird names. I'm sure there's a way to fix that, but I     │
-    //  │ haven't been able to. So I'm hand-rolling these for now.                     │
-    //  ╰──────────────────────────────────────────────────────────────────────────────╯
-    #[repr(C)]
-    #[derive(Clone, Copy, Debug)]
-    pub struct yp_string_t {
-        pub type_: yp_string_t_type,
-        pub source: *const c_char,
-        pub length: usize,
-    }
-
-    #[repr(u32)]
-    #[non_exhaustive]
-    #[derive(Clone, Copy, Debug)]
-    pub enum yp_string_t_type {
-        YP_STRING_SHARED,
-        YP_STRING_OWNED,
-        YP_STRING_CONSTANT,
-        YP_STRING_MAPPED,
-    }
 }
 
 pub use self::bindings::*;
diff --git a/src/yarp.c b/src/yarp.c
index 0529096c..4b1e7a3c 100644
--- a/src/yarp.c
+++ b/src/yarp.c
@@ -9556,26 +9556,10 @@ parse_heredoc_dedent(yp_parser_t *parser, yp_node_t *node, yp_heredoc_quote_t qu
     int common_whitespace;
     if ((common_whitespace = parse_heredoc_common_whitespace(parser, nodes)) <= 0) return;
 
-    // The next node should be dedented if it's the first node in the list or if
-    // if follows a string node.
-    bool dedent_next = true;
-
-    // Iterate over all nodes, and trim whitespace accordingly. We're going to
-    // keep around two indices: a read and a write. If we end up trimming all of
-    // the whitespace from a node, then we'll drop it from the list entirely.
-    size_t write_index = 0;
-
-    for (size_t read_index = 0; read_index < nodes->size; read_index++) {
-        yp_node_t *node = nodes->nodes[read_index];
-
-        // We're not manipulating child nodes that aren't strings. In this case
-        // we'll skip past it and indicate that the subsequent node should not
-        // be dedented.
-        if (!YP_NODE_TYPE_P(node, YP_NODE_STRING_NODE)) {
-            nodes->nodes[write_index++] = node;
-            dedent_next = false;
-            continue;
-        }
+    // Iterate over all nodes, and trim whitespace accordingly.
+    for (size_t index = 0; index < nodes->size; index++) {
+        yp_node_t *node = nodes->nodes[index];
+        if (!YP_NODE_TYPE_P(node, YP_NODE_STRING_NODE)) continue;
 
         // Get a reference to the string struct that is being held by the string
         // node. This is the value we're going to actual manipulate.
@@ -9595,6 +9579,7 @@ parse_heredoc_dedent(yp_parser_t *parser, yp_node_t *node, yp_heredoc_quote_t qu
         // whitespace, so we'll maintain a pointer to the current position in the
         // string that we're writing to.
         char *dest_cursor = source_start;
+        bool dedent_next = (index == 0) || YP_NODE_TYPE_P(nodes->nodes[index - 1], YP_NODE_STRING_NODE);
 
         while (source_cursor < source_end) {
             // If we need to dedent the next element within the heredoc or the next
@@ -9639,20 +9624,9 @@ parse_heredoc_dedent(yp_parser_t *parser, yp_node_t *node, yp_heredoc_quote_t qu
             dedent_next = true;
         }
 
-        // We only want to write this node into the list if it has any content.
-        if (dest_length == 0) {
-            yp_node_destroy(parser, node);
-        } else {
-            string->length = dest_length;
-            yp_unescape_manipulate_string(parser, string, (quote == YP_HEREDOC_QUOTE_SINGLE) ? YP_UNESCAPE_MINIMAL : YP_UNESCAPE_ALL, &parser->error_list);
-            nodes->nodes[write_index++] = node;
-        }
-
-        // We always dedent the next node if it follows a string node.
-        dedent_next = true;
+        string->length = dest_length;
+        yp_unescape_manipulate_string(parser, string, (quote == YP_HEREDOC_QUOTE_SINGLE) ? YP_UNESCAPE_MINIMAL : YP_UNESCAPE_ALL, &parser->error_list);
     }
-
-    nodes->size = write_index;
 }
 
 static yp_node_t *


Test patch:
diff --git a/rust/yarp-sys/tests/node_tests.rs b/rust/yarp-sys/tests/node_tests.rs
index 83ef5c81..19ed7377 100644
--- a/rust/yarp-sys/tests/node_tests.rs
+++ b/rust/yarp-sys/tests/node_tests.rs
@@ -1,15 +1,12 @@
 use std::{ffi::CString, mem::MaybeUninit};
 
-use yarp_sys::{
-    yp_memsize_t, yp_node_destroy, yp_node_memsize, yp_node_type, yp_parse, yp_parser_free,
-    yp_parser_init, yp_parser_t,
-};
+use yarp_sys::{yp_parser_t, yp_parser_init, yp_parse, yp_parser_free};
+use yarp_sys::{yp_node_type, yp_node_destroy};
 
 #[test]
 fn node_test() {
     let mut parser = MaybeUninit::<yp_parser_t>::uninit();
     let code = CString::new("class Foo; end").unwrap();
-    let mut memsize = MaybeUninit::<yp_memsize_t>::uninit();
 
     unsafe {
         yp_parser_init(
@@ -22,15 +19,7 @@ fn node_test() {
         let parser = parser.assume_init_mut();
         let parsed_node = yp_parse(parser);
 
-        assert_eq!(
-            (*parsed_node).type_,
-            yp_node_type::YP_NODE_PROGRAM_NODE as u16
-        );
-
-        yp_node_memsize(parsed_node, memsize.as_mut_ptr());
-        let memsize = memsize.assume_init();
-        assert_eq!(memsize.memsize, 296);
-        assert_eq!(memsize.node_count, 4);
+        assert_eq!((*parsed_node).type_, yp_node_type::YP_NODE_PROGRAM_NODE as u16);
 
         yp_node_destroy(parser, parsed_node);
         yp_parser_free(parser);
diff --git a/rust/yarp-sys/tests/parser_tests.rs b/rust/yarp-sys/tests/parser_tests.rs
index 88996702..84e5fd7d 100644
--- a/rust/yarp-sys/tests/parser_tests.rs
+++ b/rust/yarp-sys/tests/parser_tests.rs
@@ -2,15 +2,14 @@ use std::{
     ffi::{c_char, CStr, CString},
     mem::MaybeUninit,
     path::Path,
-    slice, str,
     sync::OnceLock,
 };
 
 use yarp_sys::{
-    yp_buffer_free, yp_buffer_init, yp_buffer_t, yp_comment_t, yp_comment_type_t, yp_diagnostic_t,
+    yp_comment_t, yp_comment_type_t, yp_diagnostic_t,
     yp_encoding_ascii, yp_encoding_t, yp_node_destroy, yp_parse, yp_parser_free, yp_parser_init,
     yp_parser_register_encoding_changed_callback, yp_parser_register_encoding_decode_callback,
-    yp_parser_t, yp_prettyprint,
+    yp_parser_t,
 };
 
 fn ruby_file_contents() -> (CString, usize) {
@@ -36,33 +35,6 @@ fn init_test() {
     }
 }
 
-#[test]
-fn parse_and_print_test() {
-    let (ruby_file_contents, len) = ruby_file_contents();
-    let source = ruby_file_contents.as_ptr();
-    let mut parser = MaybeUninit::<yp_parser_t>::uninit();
-    let mut buffer = MaybeUninit::<yp_buffer_t>::uninit();
-
-    unsafe {
-        yp_parser_init(parser.as_mut_ptr(), source, len, std::ptr::null());
-        let parser = parser.assume_init_mut();
-        let node = yp_parse(parser);
-
-        assert!(yp_buffer_init(buffer.as_mut_ptr()), "Failed to init buffer");
-
-        let buffer = buffer.assume_init_mut();
-        yp_prettyprint(parser, node, buffer);
-
-        let slice = slice::from_raw_parts(buffer.value.cast::<u8>(), buffer.length);
-        let string = str::from_utf8(slice).unwrap();
-        assert!(string.starts_with("ProgramNode"));
-
-        yp_node_destroy(parser, node);
-        yp_parser_free(parser);
-        yp_buffer_free(buffer);
-    }
-}
-
 #[test]
 fn comments_test() {
     let source = CString::new("# Meow!").unwrap();
@@ -110,7 +82,6 @@ fn diagnostics_test() {
         let node = yp_parse(parser);
 
         let error_list = &parser.error_list;
-        // TODO: error_list.head used to get set, but after rebasing `87e02c0b`, this behavior changed. (This pointer used to not be null).
         assert!(!error_list.head.is_null());
 
         let error = error_list.head as *const yp_diagnostic_t;
@@ -159,8 +130,6 @@ fn encoding_change_test() {
         yp_parser_register_encoding_changed_callback(parser, Some(callback));
 
         let node = yp_parse(parser);
-        // TODO: This used to get set (assumingly from encountering the 'ascii' encoding directive
-        // in `source`), but after rebasing `87e02c0b`, this behavior changed.
         assert!(parser.encoding_changed);
 
         // This value should have been mutated inside the callback when the encoding changed.
@@ -200,7 +169,7 @@ fn encoding_decode_test() {
 
     static THING: OnceLock<Output> = OnceLock::new();
 
-    let source = CString::new("# encoding: meow").unwrap();
+    let source = CString::new("# encoding: ascii").unwrap();
     let mut parser = MaybeUninit::<yp_parser_t>::uninit();
 
     unsafe {
@@ -215,8 +184,6 @@ fn encoding_decode_test() {
         yp_parser_register_encoding_decode_callback(parser, Some(callback));
 
         let node = yp_parse(parser);
-        // TODO: parser.encoding.name used to get set to "ascii" (via the callback),
-        // but stopped after I rebased on `87e02c0b`.
         assert!(!parser.encoding.name.is_null());
         assert!(!yp_encoding_ascii.name.is_null());
         assert_eq!(
@@ -225,8 +192,8 @@ fn encoding_decode_test() {
         );
 
         let output = THING.get().unwrap();
-        assert_eq!(&output.name, "meow");
-        assert_eq!(output.width, 4);
+        assert_eq!(&output.name, "ascii");
+        assert_eq!(output.width, 5);
 
         yp_node_destroy(parser, node);
         yp_parser_free(parser);
diff --git a/rust/yarp-sys/tests/serialize_tests.rs b/rust/yarp-sys/tests/serialize_tests.rs
deleted file mode 100644
index 19ddbd65..00000000
--- a/rust/yarp-sys/tests/serialize_tests.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-use std::{
-    ffi::{c_char, CString},
-    mem::MaybeUninit,
-    path::Path,
-    slice, str,
-};
-
-use yarp_sys::{
-    yp_buffer_free, yp_buffer_init, yp_buffer_t, yp_node_destroy, yp_parse, yp_parse_serialize,
-    yp_parser_free, yp_parser_init, yp_parser_t, yp_serialize,
-};
-
-fn ruby_file_contents() -> (CString, usize) {
-    let rust_path = Path::new(env!("CARGO_MANIFEST_DIR"));
-    let ruby_file_path = rust_path.join("../../lib/yarp.rb").canonicalize().unwrap();
-    let file_contents = std::fs::read_to_string(ruby_file_path).unwrap();
-    let len = file_contents.len();
-
-    (CString::new(file_contents).unwrap(), len)
-}
-
-#[test]
-fn serialize_test() {
-    let (ruby_file_contents, len) = ruby_file_contents();
-    let source = ruby_file_contents.as_ptr();
-    let mut parser = MaybeUninit::<yp_parser_t>::uninit();
-    let mut buffer = MaybeUninit::<yp_buffer_t>::uninit();
-
-    unsafe {
-        yp_parser_init(parser.as_mut_ptr(), source, len, std::ptr::null());
-        let parser = parser.assume_init_mut();
-        let node = yp_parse(parser);
-
-        assert!(yp_buffer_init(buffer.as_mut_ptr()), "Failed to init buffer");
-
-        let buffer = buffer.assume_init_mut();
-        yp_serialize(parser, node, buffer);
-
-        let serialized = std::slice::from_raw_parts(buffer.value.cast::<u8>(), buffer.length);
-
-        assert_eq!(&serialized[0..4], b"YARP");
-        assert_eq!(serialized[4..5][0], 0); // YP_VERSION_MAJOR
-        assert_eq!(serialized[5..6][0], 7); // YP_VERSION_MINOR
-        assert_eq!(serialized[6..7][0], 0); // YP_VERSION_PATCH
-
-        yp_buffer_free(buffer);
-        yp_node_destroy(parser, node);
-        yp_parser_free(parser);
-    }
-}
-
-#[test]
-fn parse_serialize_test() {
-    let (ruby_file_contents, len) = ruby_file_contents();
-    let source = ruby_file_contents.as_ptr();
-    let mut parser = MaybeUninit::<yp_parser_t>::uninit();
-    let mut serialize_buffer = MaybeUninit::<yp_buffer_t>::uninit();
-    let mut parse_serialize_buffer = MaybeUninit::<yp_buffer_t>::uninit();
-
-    let serialized = unsafe {
-        yp_parser_init(parser.as_mut_ptr(), source, len, std::ptr::null());
-        let parser = parser.assume_init_mut();
-        let node = yp_parse(parser);
-
-        assert!(
-            yp_buffer_init(serialize_buffer.as_mut_ptr()),
-            "Failed to init buffer"
-        );
-
-        let serialize_buffer = serialize_buffer.assume_init_mut();
-        yp_serialize(parser, node, serialize_buffer);
-
-        yp_node_destroy(parser, node);
-        yp_parser_free(parser);
-
-        // Can't use String -> CString here because `value` contains nul bytes.
-        slice::from_raw_parts(serialize_buffer.value.cast::<u8>(), serialize_buffer.length)
-    };
-
-    unsafe {
-        assert!(
-            yp_buffer_init(parse_serialize_buffer.as_mut_ptr()),
-            "Failed to init buffer"
-        );
-
-        let parse_serialize_buffer = parse_serialize_buffer.assume_init_mut();
-        let metadata = std::ptr::null();
-
-        yp_parse_serialize(
-            serialized.as_ptr().cast::<c_char>(),
-            serialized.len(),
-            parse_serialize_buffer,
-            metadata,
-        );
-
-        let slice = slice::from_raw_parts(
-            parse_serialize_buffer.value.cast::<u8>(),
-            parse_serialize_buffer.length,
-        );
-        let string = str::from_utf8(slice).unwrap();
-        assert!(string.starts_with("YARP"));
-
-        yp_buffer_free(serialize_buffer.as_mut_ptr());
-        yp_buffer_free(parse_serialize_buffer);
-    }
-}
diff --git a/rust/yarp-sys/tests/utils_tests.rs b/rust/yarp-sys/tests/utils_tests.rs
index d88787ff..1b42d574 100644
--- a/rust/yarp-sys/tests/utils_tests.rs
+++ b/rust/yarp-sys/tests/utils_tests.rs
@@ -34,7 +34,9 @@ mod string {
     use std::ffi::c_char;
 
     use yarp_sys::{
-        yp_string_free, yp_string_length, yp_string_source, yp_string_t, yp_string_t_type,
+        yp_string_free, yp_string_length, yp_string_source, yp_string_t,
+        yp_string_t__bindgen_ty_1, YP_STRING_SHARED, YP_STRING_OWNED,
+        YP_STRING_CONSTANT, YP_STRING_MAPPED
     };
 
     use super::*;
@@ -50,12 +52,12 @@ mod string {
         }
     }
 
-    fn make_string(string_type: yp_string_t_type) -> S {
+    fn make_string(string_type: yp_string_t__bindgen_ty_1) -> S {
         let c_string = CString::new("0123456789012345").unwrap();
 
         let yp_string = yp_string_t {
             type_: string_type,
-            source: c_string.as_ptr(),
+            source: c_string.as_ptr() as *mut c_char,
             length: c_string.as_bytes().len(),
         };
 
@@ -67,7 +69,7 @@ mod string {
 
     #[test]
     fn shared_string_test() {
-        let mut s = make_string(yp_string_t_type::YP_STRING_SHARED);
+        let mut s = make_string(YP_STRING_SHARED);
 
         unsafe {
             let len = yp_string_length(&s.yp_string);
@@ -82,14 +84,14 @@ mod string {
 
     #[test]
     fn owned_string_test() {
-        let s = make_string(yp_string_t_type::YP_STRING_OWNED);
+        let s = make_string(YP_STRING_OWNED);
 
         unsafe {
             let result_len = yp_string_length(&s.yp_string);
             assert_eq!(result_len, 16);
 
             let result_start = yp_string_source(&s.yp_string);
-            assert_eq!(s.yp_string.source, result_start);
+            assert_eq!(s.yp_string.source, result_start as *mut c_char);
 
             // Don't drop the yp_string--we don't own it anymore!
         }
@@ -97,14 +99,14 @@ mod string {
 
     #[test]
     fn constant_string_test() {
-        let mut s = make_string(yp_string_t_type::YP_STRING_CONSTANT);
+        let mut s = make_string(YP_STRING_CONSTANT);
 
         unsafe {
             let result_len = yp_string_length(&s.yp_string);
             assert_eq!(result_len, 16);
 
             let result_start = yp_string_source(&s.yp_string);
-            assert_eq!(s.yp_string.source, result_start);
+            assert_eq!(s.yp_string.source, result_start as *mut c_char);
 
             yp_string_free(&mut s.yp_string);
         }
@@ -112,27 +114,14 @@ mod string {
 
     #[test]
     fn mapped_string_test() {
-        let s = make_string(yp_string_t_type::YP_STRING_MAPPED);
+        let s = make_string(YP_STRING_MAPPED);
 
         unsafe {
             let result_len = yp_string_length(&s.yp_string);
             assert_eq!(result_len, 16);
 
             let result_start = yp_string_source(&s.yp_string);
-            assert_eq!(s.yp_string.source, result_start);
+            assert_eq!(s.yp_string.source, result_start as *mut c_char);
         }
     }
 }
-
-#[test]
-fn string_list_test() {
-    use yarp_sys::{yp_string_list_free, yp_string_list_init, yp_string_list_t};
-
-    let mut list = MaybeUninit::<yp_string_list_t>::uninit();
-
-    unsafe {
-        yp_string_list_init(list.as_mut_ptr());
-        let list = list.assume_init_mut();
-        yp_string_list_free(list);
-    }
-}
diff --git a/test/snapshots/tilde_heredocs.txt b/test/snapshots/tilde_heredocs.txt
index 8ff83369..fc51879e 100644
--- a/test/snapshots/tilde_heredocs.txt
+++ b/test/snapshots/tilde_heredocs.txt
@@ -13,7 +13,8 @@ ProgramNode(0...387)(
      ),
      InterpolatedStringNode(39...59)(
        (39...45),
-       [EmbeddedStatementsNode(48...52)(
+       [StringNode(46...48)(nil, (46...48), nil, ""),
+        EmbeddedStatementsNode(48...52)(
           (48...50),
           StatementsNode(50...51)([IntegerNode(50...51)()]),
           (51...52)
@@ -122,7 +123,8 @@ ProgramNode(0...387)(
      ),
      InterpolatedStringNode(366...387)(
        (366...372),
-       [EmbeddedStatementsNode(375...379)(
+       [StringNode(373...375)(nil, (373...375), nil, ""),
+        EmbeddedStatementsNode(375...379)(
           (375...377),
           StatementsNode(377...378)([IntegerNode(377...378)()]),
           (378...379)
diff --git a/test/snapshots/unparser/corpus/semantic/dstr.txt b/test/snapshots/unparser/corpus/semantic/dstr.txt
index 1a83679d..10345e1d 100644
--- a/test/snapshots/unparser/corpus/semantic/dstr.txt
+++ b/test/snapshots/unparser/corpus/semantic/dstr.txt
@@ -80,7 +80,8 @@ ProgramNode(0...608)(
      ),
      InterpolatedStringNode(297...314)(
        (297...303),
-       [EmbeddedStatementsNode(306...309)((306...308), nil, (308...309)),
+       [StringNode(304...306)(nil, (304...306), nil, ""),
+        EmbeddedStatementsNode(306...309)((306...308), nil, (308...309)),
         StringNode(309...310)(nil, (309...310), nil, "\n")],
        (310...314)
      ),
@@ -90,7 +91,8 @@ ProgramNode(0...608)(
        StatementsNode(325...346)(
          [InterpolatedStringNode(325...346)(
             (325...331),
-            [EmbeddedStatementsNode(336...339)((336...338), nil, (338...339)),
+            [StringNode(332...336)(nil, (332...336), nil, ""),
+             EmbeddedStatementsNode(336...339)((336...338), nil, (338...339)),
              StringNode(339...340)(nil, (339...340), nil, "\n")],
             (340...346)
           )]
@@ -119,7 +121,8 @@ ProgramNode(0...608)(
        StatementsNode(398...420)(
          [InterpolatedStringNode(398...420)(
             (398...404),
-            [EmbeddedStatementsNode(409...412)((409...411), nil, (411...412)),
+            [StringNode(405...409)(nil, (405...409), nil, ""),
+             EmbeddedStatementsNode(409...412)((409...411), nil, (411...412)),
              StringNode(412...414)(nil, (412...414), nil, "a\n")],
             (414...420)
           )]
diff --git a/test/snapshots/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt b/test/snapshots/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt
index ab09f336..b9b0a9ca 100644
--- a/test/snapshots/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt
+++ b/test/snapshots/whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt
@@ -3,7 +3,8 @@ ProgramNode(0...19)(
   StatementsNode(0...19)(
     [InterpolatedStringNode(0...19)(
        (0...7),
-       [EmbeddedStatementsNode(10...13)((10...12), nil, (12...13)),
+       [StringNode(8...10)(nil, (8...10), nil, ""),
+        EmbeddedStatementsNode(10...13)((10...12), nil, (12...13)),
         StringNode(13...14)(nil, (13...14), nil, "\n")],
        (14...19)
      )]