From b9b6d4ac94ec8437c756d54d6d9373381bfa6b2f Mon Sep 17 00:00:00 2001 From: Alex Rocha Date: Tue, 13 Jan 2026 11:03:03 -0800 Subject: [PATCH] Add lifetimes --- rust/ruby-rbs/build.rs | 35 +++++++++++++++++---------- rust/ruby-rbs/src/lib.rs | 52 ++++++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 31 deletions(-) diff --git a/rust/ruby-rbs/build.rs b/rust/ruby-rbs/build.rs index 5eb249233..bd0d6d2ca 100644 --- a/rust/ruby-rbs/build.rs +++ b/rust/ruby-rbs/build.rs @@ -119,7 +119,7 @@ fn write_node_field_accessor( if field.optional { writeln!( file, - " pub fn {}(&self) -> Option<{rust_type}> {{", + " pub fn {}(&self) -> Option<{rust_type}<'a>> {{", field.name, )?; writeln!( @@ -132,14 +132,18 @@ fn write_node_field_accessor( writeln!(file, " }} else {{")?; writeln!( file, - " Some({rust_type} {{ parser: self.parser, pointer: ptr }})" + " Some({rust_type} {{ parser: self.parser, pointer: ptr, marker: PhantomData }})" )?; writeln!(file, " }}")?; } else { - writeln!(file, " pub fn {}(&self) -> {rust_type} {{", field.name)?; writeln!( file, - " {rust_type} {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }} }}", + " pub fn {}(&self) -> {rust_type}<'a> {{", + field.name + )?; + writeln!( + file, + " {rust_type} {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }}, marker: PhantomData }}", field.c_name() )?; } @@ -334,19 +338,24 @@ fn generate(config: &Config) -> Result<(), Box> { for node in &config.nodes { writeln!(file, "#[allow(dead_code)]")?; // TODO: Remove this once all nodes that need parser are implemented writeln!(file, "#[derive(Debug)]")?; - writeln!(file, "pub struct {} {{", node.rust_name)?; + writeln!(file, "pub struct {}<'a> {{", node.rust_name)?; writeln!(file, " parser: *mut rbs_parser_t,")?; writeln!( file, " pointer: *mut {},", convert_name(&node.name, CIdentifier::Type) )?; + writeln!( + file, + " marker: PhantomData<&'a mut {}>", + convert_name(&node.name, CIdentifier::Type) + )?; writeln!(file, "}}\n")?; - writeln!(file, "impl {} {{", node.rust_name)?; + writeln!(file, "impl<'a> {}<'a> {{", node.rust_name)?; writeln!(file, " /// Converts this node to a generic node.")?; writeln!(file, " #[must_use]")?; - writeln!(file, " pub fn as_node(self) -> Node {{")?; + writeln!(file, " pub fn as_node(self) -> Node<'a> {{")?; writeln!(file, " Node::{}(self)", node.variant_name())?; writeln!(file, " }}")?; writeln!(file)?; @@ -461,7 +470,7 @@ fn generate(config: &Config) -> Result<(), Box> { field.name.as_str() }; if field.optional { - writeln!(file, " pub fn {name}(&self) -> Option {{")?; + writeln!(file, " pub fn {name}(&self) -> Option> {{")?; writeln!( file, " let ptr = unsafe {{ (*self.pointer).{} }};", @@ -472,7 +481,7 @@ fn generate(config: &Config) -> Result<(), Box> { " if ptr.is_null() {{ None }} else {{ Some(Node::new(self.parser, ptr)) }}" )?; } else { - writeln!(file, " pub fn {name}(&self) -> Node {{")?; + writeln!(file, " pub fn {name}(&self) -> Node<'a> {{")?; writeln!( file, " unsafe {{ Node::new(self.parser, (*self.pointer).{}) }}", @@ -501,18 +510,18 @@ fn generate(config: &Config) -> Result<(), Box> { // Generate the Node enum to wrap all of the structs writeln!(file, "#[derive(Debug)]")?; - writeln!(file, "pub enum Node {{")?; + writeln!(file, "pub enum Node<'a> {{")?; for node in &config.nodes { let variant_name = node .rust_name .strip_suffix("Node") .unwrap_or(&node.rust_name); - writeln!(file, " {variant_name}({}),", node.rust_name)?; + writeln!(file, " {variant_name}({}<'a>),", node.rust_name)?; } writeln!(file, "}}")?; - writeln!(file, "impl Node {{")?; + writeln!(file, "impl Node<'_> {{")?; writeln!(file, " #[allow(clippy::missing_safety_doc)]")?; writeln!( file, @@ -525,7 +534,7 @@ fn generate(config: &Config) -> Result<(), Box> { writeln!( file, - " rbs_node_type::{enum_name} => Self::{}({} {{ parser, pointer: node.cast::<{c_type}>() }}),", + " rbs_node_type::{enum_name} => Self::{}({} {{ parser, pointer: node.cast::<{c_type}>(), marker: PhantomData }}),", node.variant_name(), node.rust_name, )?; diff --git a/rust/ruby-rbs/src/lib.rs b/rust/ruby-rbs/src/lib.rs index ba4130365..1ffa89513 100644 --- a/rust/ruby-rbs/src/lib.rs +++ b/rust/ruby-rbs/src/lib.rs @@ -1,6 +1,7 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); use rbs_encoding_type_t::RBS_ENCODING_UTF_8; use ruby_rbs_sys::bindings::*; +use std::marker::PhantomData; use std::sync::Once; static INIT: Once = Once::new(); @@ -13,7 +14,7 @@ static INIT: Once = Once::new(); /// let signature = parse(rbs_code.as_bytes()); /// assert!(signature.is_ok(), "Failed to parse RBS signature"); /// ``` -pub fn parse(rbs_code: &[u8]) -> Result { +pub fn parse(rbs_code: &[u8]) -> Result, String> { unsafe { INIT.call_once(|| { rbs_constant_pool_init(RBS_GLOBAL_CONSTANT_POOL, 26); @@ -33,6 +34,7 @@ pub fn parse(rbs_code: &[u8]) -> Result { let signature_node = SignatureNode { parser, pointer: signature, + marker: PhantomData, }; if result { @@ -43,7 +45,7 @@ pub fn parse(rbs_code: &[u8]) -> Result { } } -impl Drop for SignatureNode { +impl Drop for SignatureNode<'_> { fn drop(&mut self) { unsafe { rbs_parser_free(self.parser); @@ -51,7 +53,7 @@ impl Drop for SignatureNode { } } -impl KeywordNode { +impl KeywordNode<'_> { pub fn name(&self) -> &[u8] { unsafe { let constant_ptr = rbs_constant_pool_id_to_constant( @@ -68,33 +70,40 @@ impl KeywordNode { } } -pub struct NodeList { +pub struct NodeList<'a> { parser: *mut rbs_parser_t, pointer: *mut rbs_node_list_t, + marker: PhantomData<&'a mut rbs_node_list_t>, } -impl NodeList { +impl<'a> NodeList<'a> { pub fn new(parser: *mut rbs_parser_t, pointer: *mut rbs_node_list_t) -> Self { - Self { parser, pointer } + Self { + parser, + pointer, + marker: PhantomData, + } } /// Returns an iterator over the nodes. #[must_use] - pub fn iter(&self) -> NodeListIter { + pub fn iter(&self) -> NodeListIter<'a> { NodeListIter { parser: self.parser, current: unsafe { (*self.pointer).head }, + marker: PhantomData, } } } -pub struct NodeListIter { +pub struct NodeListIter<'a> { parser: *mut rbs_parser_t, current: *mut rbs_node_list_node_t, + marker: PhantomData<&'a mut rbs_node_list_node_t>, } -impl Iterator for NodeListIter { - type Item = Node; +impl<'a> Iterator for NodeListIter<'a> { + type Item = Node<'a>; fn next(&mut self) -> Option { if self.current.is_null() { @@ -108,33 +117,40 @@ impl Iterator for NodeListIter { } } -pub struct RBSHash { +pub struct RBSHash<'a> { parser: *mut rbs_parser_t, pointer: *mut rbs_hash, + marker: PhantomData<&'a mut rbs_hash>, } -impl RBSHash { +impl<'a> RBSHash<'a> { pub fn new(parser: *mut rbs_parser_t, pointer: *mut rbs_hash) -> Self { - Self { parser, pointer } + Self { + parser, + pointer, + marker: PhantomData, + } } /// Returns an iterator over the key-value pairs. #[must_use] - pub fn iter(&self) -> RBSHashIter { + pub fn iter(&self) -> RBSHashIter<'a> { RBSHashIter { parser: self.parser, current: unsafe { (*self.pointer).head }, + marker: PhantomData, } } } -pub struct RBSHashIter { +pub struct RBSHashIter<'a> { parser: *mut rbs_parser_t, current: *mut rbs_hash_node_t, + marker: PhantomData<&'a mut rbs_hash_node_t>, } -impl Iterator for RBSHashIter { - type Item = (Node, Node); +impl<'a> Iterator for RBSHashIter<'a> { + type Item = (Node<'a>, Node<'a>); fn next(&mut self) -> Option { if self.current.is_null() { @@ -222,7 +238,7 @@ impl RBSString { } } -impl SymbolNode { +impl SymbolNode<'_> { pub fn name(&self) -> &[u8] { unsafe { let constant_ptr = rbs_constant_pool_id_to_constant(