Skip to content

Commit cf511db

Browse files
committed
Handle optional field types (#71)
Read `optional: true` annotations from `config.yml` and generate `Option<T>` return types with null checks, so we don't crash at runtime. The extracted helper function centralizes the accessor generation logic for pointer-based field types.
1 parent a544847 commit cf511db

2 files changed

Lines changed: 73 additions & 101 deletions

File tree

rust/ruby-rbs/build.rs

Lines changed: 70 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ struct NodeField {
1111
name: String,
1212
c_type: String,
1313
c_name: Option<String>,
14+
#[serde(default)]
15+
optional: bool,
1416
}
1517

1618
impl NodeField {
@@ -109,6 +111,42 @@ fn convert_name(name: &str, identifier: CIdentifier) -> String {
109111
out
110112
}
111113

114+
fn write_node_field_accessor(
115+
file: &mut File,
116+
field: &NodeField,
117+
rust_type: &str,
118+
) -> std::io::Result<()> {
119+
if field.optional {
120+
writeln!(
121+
file,
122+
" pub fn {}(&self) -> Option<{}> {{",
123+
field.name, rust_type
124+
)?;
125+
writeln!(
126+
file,
127+
" let ptr = unsafe {{ (*self.pointer).{} }};",
128+
field.c_name()
129+
)?;
130+
writeln!(file, " if ptr.is_null() {{")?;
131+
writeln!(file, " None")?;
132+
writeln!(file, " }} else {{")?;
133+
writeln!(
134+
file,
135+
" Some({rust_type} {{ parser: self.parser, pointer: ptr }})"
136+
)?;
137+
writeln!(file, " }}")?;
138+
} else {
139+
writeln!(file, " pub fn {}(&self) -> {} {{", field.name, rust_type)?;
140+
writeln!(
141+
file,
142+
" {} {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }} }}",
143+
rust_type,
144+
field.c_name()
145+
)?;
146+
}
147+
writeln!(file, " }}")
148+
}
149+
112150
fn write_visit_trait(file: &mut File, config: &Config) -> Result<(), Box<dyn std::error::Error>> {
113151
writeln!(file, "/// A trait for traversing the AST using a visitor")?;
114152
writeln!(file, "pub trait Visit {{")?;
@@ -206,128 +244,62 @@ fn generate(config: &Config) -> Result<(), Box<dyn Error>> {
206244
writeln!(file, " }}")?;
207245
}
208246
"rbs_ast_comment" => {
209-
writeln!(file, " pub fn {}(&self) -> CommentNode {{", field.name)?;
210-
writeln!(
211-
file,
212-
" CommentNode {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }} }}",
213-
field.c_name()
214-
)?;
215-
writeln!(file, " }}")?;
247+
write_node_field_accessor(&mut file, field, "CommentNode")?
216248
}
217249
"rbs_ast_declarations_class_super" => {
218-
writeln!(
219-
file,
220-
" pub fn {}(&self) -> ClassSuperNode {{",
221-
field.name
222-
)?;
223-
writeln!(
224-
file,
225-
" ClassSuperNode {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }} }}",
226-
field.c_name()
227-
)?;
228-
writeln!(file, " }}")?;
229-
}
230-
"rbs_ast_symbol" => {
231-
writeln!(file, " pub fn {}(&self) -> SymbolNode {{", field.name)?;
232-
writeln!(
233-
file,
234-
" SymbolNode {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }} }}",
235-
field.c_name()
236-
)?;
237-
writeln!(file, " }}")?;
250+
write_node_field_accessor(&mut file, field, "ClassSuperNode")?
238251
}
252+
"rbs_ast_symbol" => write_node_field_accessor(&mut file, field, "SymbolNode")?,
239253
"rbs_hash" => {
240-
writeln!(file, " pub fn {}(&self) -> RBSHash {{", field.name)?;
241-
writeln!(
242-
file,
243-
" RBSHash::new(self.parser, unsafe {{ (*self.pointer).{} }})",
244-
field.c_name()
245-
)?;
246-
writeln!(file, " }}")?;
254+
write_node_field_accessor(&mut file, field, "RBSHash")?;
247255
}
248256
"rbs_location" => {
249-
writeln!(file, " pub fn {}(&self) -> RBSLocation {{", field.name)?;
250-
writeln!(
251-
file,
252-
" RBSLocation::new(unsafe {{ (*self.pointer).{} }}, self.parser)",
253-
field.c_name()
254-
)?;
255-
writeln!(file, " }}")?;
257+
write_node_field_accessor(&mut file, field, "RBSLocation")?;
256258
}
257259
"rbs_location_list" => {
258-
writeln!(
259-
file,
260-
" pub fn {}(&self) -> RBSLocationList {{",
261-
field.name
262-
)?;
263-
writeln!(
264-
file,
265-
" RBSLocationList::new(unsafe {{ (*self.pointer).{} }}, self.parser)",
266-
field.c_name()
267-
)?;
268-
writeln!(file, " }}")?;
260+
write_node_field_accessor(&mut file, field, "RBSLocationList")?;
269261
}
270262
"rbs_namespace" => {
271-
writeln!(file, " pub fn {}(&self) -> NamespaceNode {{", field.name)?;
272-
writeln!(
273-
file,
274-
" NamespaceNode {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }} }}",
275-
field.c_name()
276-
)?;
277-
writeln!(file, " }}")?;
263+
write_node_field_accessor(&mut file, field, "NamespaceNode")?;
278264
}
279265
"rbs_node" => {
280266
let name = if field.name == "type" {
281267
"type_"
282268
} else {
283269
field.name.as_str()
284270
};
285-
286-
writeln!(file, " pub fn {}(&self) -> Node {{", name)?;
287-
writeln!(
288-
file,
289-
" unsafe {{ Node::new(self.parser, (*self.pointer).{}) }}",
290-
field.c_name()
291-
)?;
271+
if field.optional {
272+
writeln!(file, " pub fn {name}(&self) -> Option<Node> {{")?;
273+
writeln!(
274+
file,
275+
" let ptr = unsafe {{ (*self.pointer).{} }};",
276+
field.c_name()
277+
)?;
278+
writeln!(
279+
file,
280+
" if ptr.is_null() {{ None }} else {{ Some(Node::new(self.parser, ptr)) }}"
281+
)?;
282+
} else {
283+
writeln!(file, " pub fn {name}(&self) -> Node {{")?;
284+
writeln!(
285+
file,
286+
" unsafe {{ Node::new(self.parser, (*self.pointer).{}) }}",
287+
field.c_name()
288+
)?;
289+
}
292290
writeln!(file, " }}")?;
293291
}
294292
"rbs_node_list" => {
295-
writeln!(file, " pub fn {}(&self) -> NodeList {{", field.name)?;
296-
writeln!(
297-
file,
298-
" NodeList::new(self.parser, unsafe {{ (*self.pointer).{} }})",
299-
field.c_name()
300-
)?;
301-
writeln!(file, " }}")?;
302-
}
303-
"rbs_keyword" => {
304-
writeln!(file, " pub fn {}(&self) -> KeywordNode {{", field.name)?;
305-
writeln!(
306-
file,
307-
" KeywordNode {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }} }}",
308-
field.c_name()
309-
)?;
310-
writeln!(file, " }}")?;
293+
write_node_field_accessor(&mut file, field, "NodeList")?;
311294
}
295+
"rbs_keyword" => write_node_field_accessor(&mut file, field, "KeywordNode")?,
312296
"rbs_type_name" => {
313-
writeln!(file, " pub fn {}(&self) -> TypeNameNode {{", field.name)?;
314-
writeln!(
315-
file,
316-
" TypeNameNode {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }} }}",
317-
field.c_name()
318-
)?;
319-
writeln!(file, " }}")?;
297+
write_node_field_accessor(&mut file, field, "TypeNameNode")?;
320298
}
321299
"rbs_types_block" => {
322-
writeln!(file, " pub fn {}(&self) -> BlockTypeNode {{", field.name)?;
323-
writeln!(
324-
file,
325-
" BlockTypeNode {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }} }}",
326-
field.c_name()
327-
)?;
328-
writeln!(file, " }}")?;
300+
write_node_field_accessor(&mut file, field, "BlockTypeNode")?
329301
}
330-
_ => eprintln!("Unknown field type: {}", field.c_type),
302+
_ => panic!("Unknown field type: {}", field.c_type),
331303
}
332304
}
333305
}
@@ -351,7 +323,7 @@ fn generate(config: &Config) -> Result<(), Box<dyn Error>> {
351323
writeln!(file, " #[allow(clippy::missing_safety_doc)]")?;
352324
writeln!(
353325
file,
354-
" pub unsafe fn new(parser: *mut rbs_parser_t, node: *mut rbs_node_t) -> Self {{"
326+
" fn new(parser: *mut rbs_parser_t, node: *mut rbs_node_t) -> Self {{"
355327
)?;
356328
writeln!(file, " match unsafe {{ (*node).type_ }} {{")?;
357329
for node in &config.nodes {

rust/ruby-rbs/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl Iterator for NodeListIter {
6464
None
6565
} else {
6666
let pointer_data = unsafe { *self.current };
67-
let node = unsafe { Node::new(self.parser, pointer_data.node) };
67+
let node = Node::new(self.parser, pointer_data.node);
6868
self.current = pointer_data.next;
6969
Some(node)
7070
}
@@ -124,8 +124,8 @@ impl Iterator for RBSHashIter {
124124
None
125125
} else {
126126
let pointer_data = unsafe { *self.current };
127-
let key = unsafe { Node::new(self.parser, pointer_data.key) };
128-
let value = unsafe { Node::new(self.parser, pointer_data.value) };
127+
let key = Node::new(self.parser, pointer_data.key);
128+
let value = Node::new(self.parser, pointer_data.value);
129129
self.current = pointer_data.next;
130130
Some((key, value))
131131
}

0 commit comments

Comments
 (0)