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.
- checksums.yaml +4 -4
- data/lib/rbs/version.rb +1 -1
- data/rbs.gemspec +2 -2
- metadata +1 -17
- data/rust/.gitignore +0 -1
- data/rust/Cargo.lock +0 -378
- data/rust/Cargo.toml +0 -7
- data/rust/ruby-rbs/Cargo.toml +0 -22
- data/rust/ruby-rbs/build.rs +0 -764
- data/rust/ruby-rbs/examples/locations.rs +0 -60
- data/rust/ruby-rbs/src/lib.rs +0 -1
- data/rust/ruby-rbs/src/node/mod.rs +0 -742
- data/rust/ruby-rbs/tests/sanity.rs +0 -47
- data/rust/ruby-rbs/vendor/rbs/config.yml +0 -1
- data/rust/ruby-rbs-sys/Cargo.toml +0 -23
- data/rust/ruby-rbs-sys/build.rs +0 -204
- data/rust/ruby-rbs-sys/src/lib.rs +0 -50
- data/rust/ruby-rbs-sys/vendor/rbs/include +0 -1
- data/rust/ruby-rbs-sys/vendor/rbs/src +0 -1
- data/rust/ruby-rbs-sys/wrapper.h +0 -1
|
@@ -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
|
-
}
|