rbs 4.0.1.dev.1 → 4.0.1.dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,742 +0,0 @@
1
- include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
2
- use rbs_encoding_type_t::RBS_ENCODING_UTF_8;
3
- use ruby_rbs_sys::bindings::*;
4
- use std::marker::PhantomData;
5
- use std::ptr::NonNull;
6
-
7
- /// Parse RBS code into an AST.
8
- ///
9
- /// ```rust
10
- /// use ruby_rbs::node::parse;
11
- /// let rbs_code = r#"type foo = "hello""#;
12
- /// let signature = parse(rbs_code.as_bytes());
13
- /// assert!(signature.is_ok(), "Failed to parse RBS signature");
14
- /// ```
15
- pub fn parse(rbs_code: &[u8]) -> Result<SignatureNode<'_>, String> {
16
- unsafe {
17
- let start_ptr = rbs_code.as_ptr() as *const std::os::raw::c_char;
18
- let end_ptr = start_ptr.add(rbs_code.len());
19
- let bytes = rbs_code.len() as i32;
20
-
21
- let raw_rbs_string_value = rbs_string_new(start_ptr, end_ptr);
22
-
23
- let encoding_ptr = &rbs_encodings[RBS_ENCODING_UTF_8 as usize] as *const rbs_encoding_t;
24
- let parser = rbs_parser_new(raw_rbs_string_value, encoding_ptr, 0, bytes);
25
-
26
- let mut signature: *mut rbs_signature_t = std::ptr::null_mut();
27
- let result = rbs_parse_signature(parser, &mut signature);
28
-
29
- let signature_node = SignatureNode {
30
- parser: NonNull::new_unchecked(parser),
31
- pointer: signature,
32
- marker: PhantomData,
33
- };
34
-
35
- if result {
36
- Ok(signature_node)
37
- } else {
38
- let error_message = (*parser)
39
- .error
40
- .as_ref()
41
- .filter(|error| !error.message.is_null())
42
- .map(|error| {
43
- std::ffi::CStr::from_ptr(error.message)
44
- .to_string_lossy()
45
- .into_owned()
46
- })
47
- .unwrap_or_else(|| String::from("Failed to parse RBS signature"));
48
-
49
- Err(error_message)
50
- }
51
- }
52
- }
53
-
54
- impl Drop for SignatureNode<'_> {
55
- fn drop(&mut self) {
56
- unsafe {
57
- rbs_parser_free(self.parser.as_ptr());
58
- }
59
- }
60
- }
61
-
62
- /// Instance variable name specification for attributes.
63
- #[derive(Debug, Clone, PartialEq, Eq)]
64
- pub enum AttrIvarName {
65
- /// The attribute has inferred instance variable (nil)
66
- Unspecified,
67
- /// The attribute has no instance variable (false)
68
- Empty,
69
- /// The attribute has instance variable with the given name
70
- Name(rbs_constant_id_t),
71
- }
72
-
73
- impl AttrIvarName {
74
- /// Converts the raw C struct to the Rust enum.
75
- #[must_use]
76
- pub fn from_raw(raw: rbs_attr_ivar_name_t) -> Self {
77
- match raw.tag {
78
- rbs_attr_ivar_name_tag::RBS_ATTR_IVAR_NAME_TAG_UNSPECIFIED => Self::Unspecified,
79
- rbs_attr_ivar_name_tag::RBS_ATTR_IVAR_NAME_TAG_EMPTY => Self::Empty,
80
- rbs_attr_ivar_name_tag::RBS_ATTR_IVAR_NAME_TAG_NAME => Self::Name(raw.name),
81
- _ => panic!("Unknown ivar_name_tag: {}", raw.tag),
82
- }
83
- }
84
- }
85
-
86
- pub struct NodeList<'a> {
87
- parser: NonNull<rbs_parser_t>,
88
- pointer: *mut rbs_node_list_t,
89
- marker: PhantomData<&'a mut rbs_node_list_t>,
90
- }
91
-
92
- impl<'a> NodeList<'a> {
93
- #[must_use]
94
- pub fn new(parser: NonNull<rbs_parser_t>, pointer: *mut rbs_node_list_t) -> Self {
95
- Self {
96
- parser,
97
- pointer,
98
- marker: PhantomData,
99
- }
100
- }
101
-
102
- /// Returns an iterator over the nodes.
103
- #[must_use]
104
- pub fn iter(&self) -> NodeListIter<'a> {
105
- NodeListIter {
106
- parser: self.parser,
107
- current: unsafe { (*self.pointer).head },
108
- marker: PhantomData,
109
- }
110
- }
111
- }
112
-
113
- pub struct NodeListIter<'a> {
114
- parser: NonNull<rbs_parser_t>,
115
- current: *mut rbs_node_list_node_t,
116
- marker: PhantomData<&'a mut rbs_node_list_node_t>,
117
- }
118
-
119
- impl<'a> Iterator for NodeListIter<'a> {
120
- type Item = Node<'a>;
121
-
122
- fn next(&mut self) -> Option<Self::Item> {
123
- if self.current.is_null() {
124
- None
125
- } else {
126
- let pointer_data = unsafe { *self.current };
127
- let node = Node::new(self.parser, pointer_data.node);
128
- self.current = pointer_data.next;
129
- Some(node)
130
- }
131
- }
132
- }
133
-
134
- pub struct RBSHash<'a> {
135
- parser: NonNull<rbs_parser_t>,
136
- pointer: *mut rbs_hash,
137
- marker: PhantomData<&'a mut rbs_hash>,
138
- }
139
-
140
- impl<'a> RBSHash<'a> {
141
- #[must_use]
142
- pub fn new(parser: NonNull<rbs_parser_t>, pointer: *mut rbs_hash) -> Self {
143
- Self {
144
- parser,
145
- pointer,
146
- marker: PhantomData,
147
- }
148
- }
149
-
150
- /// Returns an iterator over the key-value pairs.
151
- #[must_use]
152
- pub fn iter(&self) -> RBSHashIter<'a> {
153
- RBSHashIter {
154
- parser: self.parser,
155
- current: unsafe { (*self.pointer).head },
156
- marker: PhantomData,
157
- }
158
- }
159
- }
160
-
161
- pub struct RBSHashIter<'a> {
162
- parser: NonNull<rbs_parser_t>,
163
- current: *mut rbs_hash_node_t,
164
- marker: PhantomData<&'a mut rbs_hash_node_t>,
165
- }
166
-
167
- impl<'a> Iterator for RBSHashIter<'a> {
168
- type Item = (Node<'a>, Node<'a>);
169
-
170
- fn next(&mut self) -> Option<Self::Item> {
171
- if self.current.is_null() {
172
- None
173
- } else {
174
- let pointer_data = unsafe { *self.current };
175
- let key = Node::new(self.parser, pointer_data.key);
176
- let value = Node::new(self.parser, pointer_data.value);
177
- self.current = pointer_data.next;
178
- Some((key, value))
179
- }
180
- }
181
- }
182
-
183
- pub struct RBSLocationRange {
184
- range: rbs_location_range,
185
- }
186
-
187
- impl RBSLocationRange {
188
- #[must_use]
189
- pub fn new(range: rbs_location_range) -> Self {
190
- Self { range }
191
- }
192
-
193
- #[must_use]
194
- pub fn start(&self) -> i32 {
195
- self.range.start_byte
196
- }
197
-
198
- #[must_use]
199
- pub fn end(&self) -> i32 {
200
- self.range.end_byte
201
- }
202
- }
203
-
204
- pub struct RBSLocationRangeList<'a> {
205
- #[allow(dead_code)]
206
- parser: NonNull<rbs_parser_t>,
207
- pointer: *mut rbs_location_range_list_t,
208
- marker: PhantomData<&'a mut rbs_location_range_list_t>,
209
- }
210
-
211
- impl<'a> RBSLocationRangeList<'a> {
212
- /// Returns an iterator over the location ranges.
213
- #[must_use]
214
- pub fn iter(&self) -> RBSLocationRangeListIter {
215
- RBSLocationRangeListIter {
216
- current: unsafe { (*self.pointer).head },
217
- }
218
- }
219
- }
220
-
221
- pub struct RBSLocationRangeListIter {
222
- current: *mut rbs_location_range_list_node_t,
223
- }
224
-
225
- impl Iterator for RBSLocationRangeListIter {
226
- type Item = RBSLocationRange;
227
-
228
- fn next(&mut self) -> Option<Self::Item> {
229
- if self.current.is_null() {
230
- None
231
- } else {
232
- let pointer_data = unsafe { *self.current };
233
- let range = RBSLocationRange::new(pointer_data.range);
234
- self.current = pointer_data.next;
235
- Some(range)
236
- }
237
- }
238
- }
239
-
240
- #[derive(Debug)]
241
- pub struct RBSString {
242
- pointer: *const rbs_string_t,
243
- }
244
-
245
- impl RBSString {
246
- #[must_use]
247
- pub fn new(pointer: *const rbs_string_t) -> Self {
248
- Self { pointer }
249
- }
250
-
251
- #[must_use]
252
- pub fn as_bytes(&self) -> &[u8] {
253
- unsafe {
254
- let s = *self.pointer;
255
- std::slice::from_raw_parts(s.start as *const u8, s.end.offset_from(s.start) as usize)
256
- }
257
- }
258
- }
259
-
260
- impl SymbolNode<'_> {
261
- #[must_use]
262
- pub fn name(&self) -> &[u8] {
263
- unsafe {
264
- let constant_ptr = rbs_constant_pool_id_to_constant(
265
- &(*self.parser.as_ptr()).constant_pool,
266
- (*self.pointer).constant_id,
267
- );
268
- if constant_ptr.is_null() {
269
- panic!("Constant ID for symbol is not present in the pool");
270
- }
271
-
272
- let constant = &*constant_ptr;
273
- std::slice::from_raw_parts(constant.start, constant.length)
274
- }
275
- }
276
- }
277
-
278
- #[cfg(test)]
279
- mod tests {
280
- use super::*;
281
-
282
- #[test]
283
- fn test_parse_error_contains_actual_message() {
284
- let rbs_code = "class { end";
285
- let result = parse(rbs_code.as_bytes());
286
- let error_message = result.unwrap_err();
287
- assert_eq!(error_message, "expected one of class/module/constant name");
288
- }
289
-
290
- #[test]
291
- fn test_parse() {
292
- let rbs_code = r#"type foo = "hello""#;
293
- let signature = parse(rbs_code.as_bytes());
294
- assert!(signature.is_ok(), "Failed to parse RBS signature");
295
-
296
- let rbs_code2 = r#"class Foo end"#;
297
- let signature2 = parse(rbs_code2.as_bytes());
298
- assert!(signature2.is_ok(), "Failed to parse RBS signature");
299
- }
300
-
301
- #[test]
302
- fn test_parse_integer() {
303
- let rbs_code = r#"type foo = 1"#;
304
- let signature = parse(rbs_code.as_bytes());
305
- assert!(signature.is_ok(), "Failed to parse RBS signature");
306
-
307
- let signature_node = signature.unwrap();
308
- if let Node::TypeAlias(node) = signature_node.declarations().iter().next().unwrap()
309
- && let Node::LiteralType(literal) = node.type_()
310
- && let Node::Integer(integer) = literal.literal()
311
- {
312
- assert_eq!(
313
- "1".to_string(),
314
- String::from_utf8(integer.string_representation().as_bytes().to_vec()).unwrap()
315
- );
316
- } else {
317
- panic!("No literal type node found");
318
- }
319
- }
320
-
321
- #[test]
322
- fn test_rbs_hash_via_record_type() {
323
- // RecordType stores its fields in an RBSHash via all_fields()
324
- let rbs_code = r#"type foo = { name: String, age: Integer }"#;
325
- let signature = parse(rbs_code.as_bytes());
326
- assert!(signature.is_ok(), "Failed to parse RBS signature");
327
-
328
- let signature_node = signature.unwrap();
329
- if let Node::TypeAlias(type_alias) = signature_node.declarations().iter().next().unwrap()
330
- && let Node::RecordType(record) = type_alias.type_()
331
- {
332
- let hash = record.all_fields();
333
- let fields: Vec<_> = hash.iter().collect();
334
- assert_eq!(fields.len(), 2, "Expected 2 fields in record");
335
-
336
- // Build a map of field names to type names
337
- let mut field_types: Vec<(String, String)> = Vec::new();
338
- for (key, value) in &fields {
339
- let Node::Symbol(sym) = key else {
340
- panic!("Expected Symbol key");
341
- };
342
- let Node::RecordFieldType(field_type) = value else {
343
- panic!("Expected RecordFieldType value");
344
- };
345
- let Node::ClassInstanceType(class_type) = field_type.type_() else {
346
- panic!("Expected ClassInstanceType");
347
- };
348
-
349
- let key_name = String::from_utf8(sym.name().to_vec()).unwrap();
350
- let type_name_node = class_type.name();
351
- let type_name_sym = type_name_node.name();
352
- let type_name = String::from_utf8(type_name_sym.name().to_vec()).unwrap();
353
- field_types.push((key_name, type_name));
354
- }
355
-
356
- assert!(
357
- field_types.contains(&("name".to_string(), "String".to_string())),
358
- "Expected 'name: String'"
359
- );
360
- assert!(
361
- field_types.contains(&("age".to_string(), "Integer".to_string())),
362
- "Expected 'age: Integer'"
363
- );
364
- } else {
365
- panic!("Expected TypeAlias with RecordType");
366
- }
367
- }
368
-
369
- #[test]
370
- fn visitor_test() {
371
- struct Visitor {
372
- visited: Vec<String>,
373
- }
374
-
375
- impl Visit for Visitor {
376
- fn visit_bool_type_node(&mut self, node: &BoolTypeNode) {
377
- self.visited.push("type:bool".to_string());
378
-
379
- crate::node::visit_bool_type_node(self, node);
380
- }
381
-
382
- fn visit_class_node(&mut self, node: &ClassNode) {
383
- self.visited.push(format!(
384
- "class:{}",
385
- String::from_utf8(node.name().name().name().to_vec()).unwrap()
386
- ));
387
-
388
- crate::node::visit_class_node(self, node);
389
- }
390
-
391
- fn visit_class_instance_type_node(&mut self, node: &ClassInstanceTypeNode) {
392
- self.visited.push(format!(
393
- "type:{}",
394
- String::from_utf8(node.name().name().name().to_vec()).unwrap()
395
- ));
396
-
397
- crate::node::visit_class_instance_type_node(self, node);
398
- }
399
-
400
- fn visit_class_super_node(&mut self, node: &ClassSuperNode) {
401
- self.visited.push(format!(
402
- "super:{}",
403
- String::from_utf8(node.name().name().name().to_vec()).unwrap()
404
- ));
405
-
406
- crate::node::visit_class_super_node(self, node);
407
- }
408
-
409
- fn visit_function_type_node(&mut self, node: &FunctionTypeNode) {
410
- let count = node.required_positionals().iter().count();
411
- self.visited
412
- .push(format!("function:required_positionals:{count}"));
413
-
414
- crate::node::visit_function_type_node(self, node);
415
- }
416
-
417
- fn visit_method_definition_node(&mut self, node: &MethodDefinitionNode) {
418
- self.visited.push(format!(
419
- "method:{}",
420
- String::from_utf8(node.name().name().to_vec()).unwrap()
421
- ));
422
-
423
- crate::node::visit_method_definition_node(self, node);
424
- }
425
-
426
- fn visit_record_type_node(&mut self, node: &RecordTypeNode) {
427
- self.visited.push("record".to_string());
428
-
429
- crate::node::visit_record_type_node(self, node);
430
- }
431
-
432
- fn visit_symbol_node(&mut self, node: &SymbolNode) {
433
- self.visited.push(format!(
434
- "symbol:{}",
435
- String::from_utf8(node.name().to_vec()).unwrap()
436
- ));
437
-
438
- crate::node::visit_symbol_node(self, node);
439
- }
440
- }
441
-
442
- let rbs_code = r#"
443
- class Foo < Bar
444
- def process: ({ name: String, age: Integer }, bool) -> void
445
- end
446
- "#;
447
-
448
- let signature = parse(rbs_code.as_bytes()).unwrap();
449
-
450
- let mut visitor = Visitor {
451
- visited: Vec::new(),
452
- };
453
-
454
- visitor.visit(&signature.as_node());
455
-
456
- assert_eq!(
457
- vec![
458
- "class:Foo",
459
- "symbol:Foo",
460
- "super:Bar",
461
- "symbol:Bar",
462
- "method:process",
463
- "symbol:process",
464
- "function:required_positionals:2",
465
- "record",
466
- "symbol:name",
467
- "type:String",
468
- "symbol:String",
469
- "symbol:age",
470
- "type:Integer",
471
- "symbol:Integer",
472
- "type:bool",
473
- ],
474
- visitor.visited
475
- );
476
- }
477
-
478
- #[test]
479
- fn test_node_location_ranges() {
480
- let rbs_code = r#"type foo = 1"#;
481
- let signature = parse(rbs_code.as_bytes()).unwrap();
482
-
483
- let declaration = signature.declarations().iter().next().unwrap();
484
- let Node::TypeAlias(type_alias) = declaration else {
485
- panic!("Expected TypeAlias");
486
- };
487
-
488
- // TypeAlias spans the entire declaration
489
- let loc = type_alias.location();
490
- assert_eq!(0, loc.start());
491
- assert_eq!(12, loc.end());
492
-
493
- // The literal "1" is at position 11-12
494
- let Node::LiteralType(literal) = type_alias.type_() else {
495
- panic!("Expected LiteralType");
496
- };
497
- let Node::Integer(integer) = literal.literal() else {
498
- panic!("Expected Integer");
499
- };
500
-
501
- let int_loc = integer.location();
502
- assert_eq!(11, int_loc.start());
503
- assert_eq!(12, int_loc.end());
504
- }
505
-
506
- #[test]
507
- fn test_sub_locations() {
508
- let rbs_code = r#"class Foo < Bar end"#;
509
- let signature = parse(rbs_code.as_bytes()).unwrap();
510
-
511
- let declaration = signature.declarations().iter().next().unwrap();
512
- let Node::Class(class) = declaration else {
513
- panic!("Expected Class");
514
- };
515
-
516
- // Test required sub-locations
517
- let keyword_loc = class.keyword_location();
518
- assert_eq!(0, keyword_loc.start());
519
- assert_eq!(5, keyword_loc.end());
520
-
521
- let name_loc = class.name_location();
522
- assert_eq!(6, name_loc.start());
523
- assert_eq!(9, name_loc.end());
524
-
525
- let end_loc = class.end_location();
526
- assert_eq!(16, end_loc.start());
527
- assert_eq!(19, end_loc.end());
528
-
529
- // Test optional sub-location that's present
530
- let lt_loc = class.lt_location();
531
- assert!(lt_loc.is_some());
532
- let lt = lt_loc.unwrap();
533
- assert_eq!(10, lt.start());
534
- assert_eq!(11, lt.end());
535
-
536
- // Test optional sub-location that's not present (no type params in this class)
537
- let type_params_loc = class.type_params_location();
538
- assert!(type_params_loc.is_none());
539
- }
540
-
541
- #[test]
542
- fn test_type_alias_sub_locations() {
543
- let rbs_code = r#"type foo = String"#;
544
- let signature = parse(rbs_code.as_bytes()).unwrap();
545
-
546
- let declaration = signature.declarations().iter().next().unwrap();
547
- let Node::TypeAlias(type_alias) = declaration else {
548
- panic!("Expected TypeAlias");
549
- };
550
-
551
- // Test required sub-locations
552
- let keyword_loc = type_alias.keyword_location();
553
- assert_eq!(0, keyword_loc.start());
554
- assert_eq!(4, keyword_loc.end());
555
-
556
- let name_loc = type_alias.name_location();
557
- assert_eq!(5, name_loc.start());
558
- assert_eq!(8, name_loc.end());
559
-
560
- let eq_loc = type_alias.eq_location();
561
- assert_eq!(9, eq_loc.start());
562
- assert_eq!(10, eq_loc.end());
563
-
564
- // Test optional sub-location that's not present (no type params)
565
- let type_params_loc = type_alias.type_params_location();
566
- assert!(type_params_loc.is_none());
567
- }
568
-
569
- #[test]
570
- fn test_module_sub_locations() {
571
- let rbs_code = r#"module Foo[T] : Bar end"#;
572
- let signature = parse(rbs_code.as_bytes()).unwrap();
573
-
574
- let declaration = signature.declarations().iter().next().unwrap();
575
- let Node::Module(module) = declaration else {
576
- panic!("Expected Module");
577
- };
578
-
579
- // Test required sub-locations
580
- let keyword_loc = module.keyword_location();
581
- assert_eq!(0, keyword_loc.start());
582
- assert_eq!(6, keyword_loc.end());
583
-
584
- let name_loc = module.name_location();
585
- assert_eq!(7, name_loc.start());
586
- assert_eq!(10, name_loc.end());
587
-
588
- let end_loc = module.end_location();
589
- assert_eq!(20, end_loc.start());
590
- assert_eq!(23, end_loc.end());
591
-
592
- // Test optional sub-locations that are present
593
- let type_params_loc = module.type_params_location();
594
- assert!(type_params_loc.is_some());
595
- let tp = type_params_loc.unwrap();
596
- assert_eq!(10, tp.start());
597
- assert_eq!(13, tp.end());
598
-
599
- let colon_loc = module.colon_location();
600
- assert!(colon_loc.is_some());
601
- let colon = colon_loc.unwrap();
602
- assert_eq!(14, colon.start());
603
- assert_eq!(15, colon.end());
604
-
605
- let self_types_loc = module.self_types_location();
606
- assert!(self_types_loc.is_some());
607
- let st = self_types_loc.unwrap();
608
- assert_eq!(16, st.start());
609
- assert_eq!(19, st.end());
610
- }
611
-
612
- #[test]
613
- fn test_enum_types() {
614
- let rbs_code = r#"
615
- class Foo
616
- attr_reader name: String
617
- def self.process: () -> void
618
- alias instance_method target_method
619
- alias self.singleton_method self.target_method
620
- end
621
-
622
- class Bar[out T, in U, V]
623
- end
624
- "#;
625
- let signature = parse(rbs_code.as_bytes()).unwrap();
626
-
627
- let declarations: Vec<_> = signature.declarations().iter().collect();
628
-
629
- // Test class Foo
630
- let Node::Class(class_foo) = &declarations[0] else {
631
- panic!("Expected Class");
632
- };
633
-
634
- let members: Vec<_> = class_foo.members().iter().collect();
635
-
636
- // attr_reader - should be instance with unspecified visibility (default)
637
- if let Node::AttrReader(attr) = &members[0] {
638
- assert_eq!(attr.kind(), AttributeKind::Instance);
639
- assert_eq!(attr.visibility(), AttributeVisibility::Unspecified);
640
- } else {
641
- panic!("Expected AttrReader");
642
- }
643
-
644
- // def self.process - should be singleton method with unspecified visibility (default)
645
- if let Node::MethodDefinition(method) = &members[1] {
646
- assert_eq!(method.kind(), MethodDefinitionKind::Singleton);
647
- assert_eq!(method.visibility(), MethodDefinitionVisibility::Unspecified);
648
- } else {
649
- panic!("Expected MethodDefinition");
650
- }
651
-
652
- // alias instance_method
653
- if let Node::Alias(alias) = &members[2] {
654
- assert_eq!(alias.kind(), AliasKind::Instance);
655
- } else {
656
- panic!("Expected Alias");
657
- }
658
-
659
- // alias self.singleton_method
660
- if let Node::Alias(alias) = &members[3] {
661
- assert_eq!(alias.kind(), AliasKind::Singleton);
662
- } else {
663
- panic!("Expected Alias");
664
- }
665
-
666
- // Test class Bar with type params
667
- let Node::Class(class_bar) = &declarations[1] else {
668
- panic!("Expected Class");
669
- };
670
-
671
- let type_params: Vec<_> = class_bar.type_params().iter().collect();
672
- assert_eq!(type_params.len(), 3);
673
-
674
- // out T - covariant
675
- if let Node::TypeParam(param) = &type_params[0] {
676
- assert_eq!(param.variance(), TypeParamVariance::Covariant);
677
- } else {
678
- panic!("Expected TypeParam");
679
- }
680
-
681
- // in U - contravariant
682
- if let Node::TypeParam(param) = &type_params[1] {
683
- assert_eq!(param.variance(), TypeParamVariance::Contravariant);
684
- } else {
685
- panic!("Expected TypeParam");
686
- }
687
-
688
- // V - invariant (default)
689
- if let Node::TypeParam(param) = &type_params[2] {
690
- assert_eq!(param.variance(), TypeParamVariance::Invariant);
691
- } else {
692
- panic!("Expected TypeParam");
693
- }
694
- }
695
-
696
- #[test]
697
- fn test_ivar_name_enum() {
698
- let rbs_code = r#"
699
- class Foo
700
- attr_reader name: String
701
- attr_accessor age(): Integer
702
- attr_writer email(@email): String
703
- end
704
- "#;
705
- let signature = parse(rbs_code.as_bytes()).unwrap();
706
-
707
- let Node::Class(class) = signature.declarations().iter().next().unwrap() else {
708
- panic!("Expected Class");
709
- };
710
-
711
- let members: Vec<_> = class.members().iter().collect();
712
-
713
- // attr_reader name: String - should be Unspecified (inferred as @name)
714
- if let Node::AttrReader(attr) = &members[0] {
715
- let ivar = attr.ivar_name();
716
- assert_eq!(ivar, AttrIvarName::Unspecified);
717
- } else {
718
- panic!("Expected AttrReader");
719
- }
720
-
721
- // attr_accessor age(): Integer - should be Empty (no ivar)
722
- if let Node::AttrAccessor(attr) = &members[1] {
723
- let ivar = attr.ivar_name();
724
- assert_eq!(ivar, AttrIvarName::Empty);
725
- } else {
726
- panic!("Expected AttrAccessor");
727
- }
728
-
729
- // attr_writer email(@email): String - should be Name with constant ID
730
- if let Node::AttrWriter(attr) = &members[2] {
731
- let ivar = attr.ivar_name();
732
- match ivar {
733
- AttrIvarName::Name(id) => {
734
- assert!(id > 0, "Expected valid constant ID");
735
- }
736
- _ => panic!("Expected AttrIvarName::Name, got {:?}", ivar),
737
- }
738
- } else {
739
- panic!("Expected AttrWriter");
740
- }
741
- }
742
- }