1#![warn(clippy::all, clippy::nursery, clippy::pedantic, future_incompatible, missing_docs, nonstandard_style, rust_2018_idioms, trivial_casts, trivial_numeric_casts, unreachable_pub, unused_qualifications)]
6
7#[allow(clippy::too_many_lines, clippy::use_self)]
10mod bindings {
11 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
14}
15
16mod node;
17mod parse_result;
18
19use std::mem::MaybeUninit;
20use std::ptr::NonNull;
21
22pub use self::bindings::*;
23pub use self::node::{ConstantId, ConstantList, ConstantListIter, Integer, NodeList, NodeListIter};
24pub use self::parse_result::{Comment, CommentType, Comments, Diagnostic, Diagnostics, Location, MagicComment, MagicComments, ParseResult};
25
26use ruby_prism_sys::{pm_parse, pm_parser_init, pm_parser_t};
27
28#[must_use]
35pub fn parse(source: &[u8]) -> ParseResult<'_> {
36 unsafe {
37 let uninit = Box::new(MaybeUninit::<pm_parser_t>::uninit());
38 let uninit = Box::into_raw(uninit);
39
40 pm_parser_init((*uninit).as_mut_ptr(), source.as_ptr(), source.len(), std::ptr::null());
41
42 let parser = (*uninit).assume_init_mut();
43 let parser = NonNull::new_unchecked(parser);
44
45 let node = pm_parse(parser.as_ptr());
46 let node = NonNull::new_unchecked(node);
47
48 ParseResult::new(source, parser, node)
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::parse;
55
56 #[test]
57 fn comments_test() {
58 let source = "# comment 1\n# comment 2\n# comment 3\n";
59 let result = parse(source.as_ref());
60
61 for comment in result.comments() {
62 assert_eq!(super::CommentType::InlineComment, comment.type_());
63 let text = std::str::from_utf8(comment.text()).unwrap();
64 assert!(text.starts_with("# comment"));
65 }
66 }
67
68 #[test]
69 fn magic_comments_test() {
70 use crate::MagicComment;
71
72 let source = "# typed: ignore\n# typed:true\n#typed: strict\n";
73 let result = parse(source.as_ref());
74
75 let comments: Vec<MagicComment<'_>> = result.magic_comments().collect();
76 assert_eq!(3, comments.len());
77
78 assert_eq!(b"typed", comments[0].key());
79 assert_eq!(b"ignore", comments[0].value());
80
81 assert_eq!(b"typed", comments[1].key());
82 assert_eq!(b"true", comments[1].value());
83
84 assert_eq!(b"typed", comments[2].key());
85 assert_eq!(b"strict", comments[2].value());
86 }
87
88 #[test]
89 fn data_loc_test() {
90 let source = "1";
91 let result = parse(source.as_ref());
92 let data_loc = result.data_loc();
93 assert!(data_loc.is_none());
94
95 let source = "__END__\nabc\n";
96 let result = parse(source.as_ref());
97 let data_loc = result.data_loc().unwrap();
98 let slice = std::str::from_utf8(result.as_slice(&data_loc)).unwrap();
99 assert_eq!(slice, "__END__\nabc\n");
100
101 let source = "1\n2\n3\n__END__\nabc\ndef\n";
102 let result = parse(source.as_ref());
103 let data_loc = result.data_loc().unwrap();
104 let slice = std::str::from_utf8(result.as_slice(&data_loc)).unwrap();
105 assert_eq!(slice, "__END__\nabc\ndef\n");
106 }
107
108 #[test]
109 fn location_test() {
110 let source = "111 + 222 + 333";
111 let result = parse(source.as_ref());
112
113 let node = result.node();
114 let node = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
115 let node = node.as_call_node().unwrap().receiver().unwrap();
116 let plus = node.as_call_node().unwrap();
117 let node = plus.arguments().unwrap().arguments().iter().next().unwrap();
118
119 let location = node.as_integer_node().unwrap().location();
120 let slice = std::str::from_utf8(result.as_slice(&location)).unwrap();
121
122 assert_eq!(slice, "222");
123 assert_eq!(6, location.start);
124 assert_eq!(9, location.end());
125
126 let recv_loc = plus.receiver().unwrap().location();
127 assert_eq!(recv_loc.as_slice(), b"111");
128 assert_eq!(0, recv_loc.start);
129 assert_eq!(3, recv_loc.end());
130
131 let joined = recv_loc.join(&location).unwrap();
132 assert_eq!(joined.as_slice(), b"111 + 222");
133
134 let not_joined = location.join(&recv_loc);
135 assert!(not_joined.is_none());
136
137 {
138 let result = parse(source.as_ref());
139 let node = result.node();
140 let node = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
141 let node = node.as_call_node().unwrap().receiver().unwrap();
142 let plus = node.as_call_node().unwrap();
143 let node = plus.arguments().unwrap().arguments().iter().next().unwrap();
144
145 let location = node.as_integer_node().unwrap().location();
146 let not_joined = recv_loc.join(&location);
147 assert!(not_joined.is_none());
148
149 let not_joined = location.join(&recv_loc);
150 assert!(not_joined.is_none());
151 }
152
153 let location = node.location();
154 let slice = std::str::from_utf8(result.as_slice(&location)).unwrap();
155
156 assert_eq!(slice, "222");
157
158 let slice = std::str::from_utf8(location.as_slice()).unwrap();
159
160 assert_eq!(slice, "222");
161 }
162
163 #[test]
164 fn visitor_test() {
165 use super::{visit_interpolated_regular_expression_node, visit_regular_expression_node, InterpolatedRegularExpressionNode, RegularExpressionNode, Visit};
166
167 struct RegularExpressionVisitor {
168 count: usize,
169 }
170
171 impl Visit<'_> for RegularExpressionVisitor {
172 fn visit_interpolated_regular_expression_node(&mut self, node: &InterpolatedRegularExpressionNode<'_>) {
173 self.count += 1;
174 visit_interpolated_regular_expression_node(self, node);
175 }
176
177 fn visit_regular_expression_node(&mut self, node: &RegularExpressionNode<'_>) {
178 self.count += 1;
179 visit_regular_expression_node(self, node);
180 }
181 }
182
183 let source = "# comment 1\n# comment 2\nmodule Foo; class Bar; /abc #{/def/}/; end; end";
184 let result = parse(source.as_ref());
185
186 let mut visitor = RegularExpressionVisitor { count: 0 };
187 visitor.visit(&result.node());
188
189 assert_eq!(visitor.count, 2);
190 }
191
192 #[test]
193 fn node_upcast_test() {
194 use super::Node;
195
196 let source = "module Foo; end";
197 let result = parse(source.as_ref());
198
199 let node = result.node();
200 let upcast_node = node.as_program_node().unwrap().as_node();
201 assert!(matches!(upcast_node, Node::ProgramNode { .. }));
202
203 let node = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
204 let upcast_node = node.as_module_node().unwrap().as_node();
205 assert!(matches!(upcast_node, Node::ModuleNode { .. }));
206 }
207
208 #[test]
209 fn constant_id_test() {
210 let source = "module Foo; x = 1; y = 2; end";
211 let result = parse(source.as_ref());
212
213 let node = result.node();
214 assert_eq!(node.as_program_node().unwrap().statements().body().len(), 1);
215 assert!(!node.as_program_node().unwrap().statements().body().is_empty());
216 let module = node.as_program_node().and_then(|pn| pn.statements().body().first()).unwrap();
217 let module = module.as_module_node().unwrap();
218
219 assert_eq!(module.locals().len(), 2);
220 assert!(!module.locals().is_empty());
221
222 assert_eq!(module.locals().first().unwrap().as_slice(), b"x");
223 assert_eq!(module.locals().last().unwrap().as_slice(), b"y");
224
225 let source = "module Foo; end";
226 let result = parse(source.as_ref());
227
228 let node = result.node();
229 assert_eq!(node.as_program_node().unwrap().statements().body().len(), 1);
230 assert!(!node.as_program_node().unwrap().statements().body().is_empty());
231 let module = node.as_program_node().and_then(|pn| pn.statements().body().first()).unwrap();
232 let module = module.as_module_node().unwrap();
233
234 assert_eq!(module.locals().len(), 0);
235 assert!(module.locals().is_empty());
236 }
237
238 #[test]
239 fn optional_loc_test() {
240 let source = r"
241module Example
242 x = call_func(3, 4)
243 y = x.call_func 5, 6
244end
245";
246 let result = parse(source.as_ref());
247
248 let node = result.node();
249 let module = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
250 let module = module.as_module_node().unwrap();
251 let body = module.body();
252 let writes = body.iter().next().unwrap().as_statements_node().unwrap().body().iter().collect::<Vec<_>>();
253 assert_eq!(writes.len(), 2);
254
255 let asgn = &writes[0];
256 let call = asgn.as_local_variable_write_node().unwrap().value();
257 let call = call.as_call_node().unwrap();
258
259 let call_operator_loc = call.call_operator_loc();
260 assert!(call_operator_loc.is_none());
261 let closing_loc = call.closing_loc();
262 assert!(closing_loc.is_some());
263
264 let asgn = &writes[1];
265 let call = asgn.as_local_variable_write_node().unwrap().value();
266 let call = call.as_call_node().unwrap();
267
268 let call_operator_loc = call.call_operator_loc();
269 assert!(call_operator_loc.is_some());
270 let closing_loc = call.closing_loc();
271 assert!(closing_loc.is_none());
272 }
273
274 #[test]
275 fn frozen_strings_test() {
276 let source = r#"
277# frozen_string_literal: true
278"foo"
279"#;
280 let result = parse(source.as_ref());
281 assert!(result.frozen_string_literals());
282
283 let source = "3";
284 let result = parse(source.as_ref());
285 assert!(!result.frozen_string_literals());
286 }
287
288 #[test]
289 fn string_flags_test() {
290 let source = r#"
291# frozen_string_literal: true
292"foo"
293"#;
294 let result = parse(source.as_ref());
295
296 let node = result.node();
297 let string = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
298 let string = string.as_string_node().unwrap();
299 assert!(string.is_frozen());
300
301 let source = r#"
302"foo"
303"#;
304 let result = parse(source.as_ref());
305
306 let node = result.node();
307 let string = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
308 let string = string.as_string_node().unwrap();
309 assert!(!string.is_frozen());
310 }
311
312 #[test]
313 fn call_flags_test() {
314 let source = r"
315x
316";
317 let result = parse(source.as_ref());
318
319 let node = result.node();
320 let call = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
321 let call = call.as_call_node().unwrap();
322 assert!(call.is_variable_call());
323
324 let source = r"
325x&.foo
326";
327 let result = parse(source.as_ref());
328
329 let node = result.node();
330 let call = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
331 let call = call.as_call_node().unwrap();
332 assert!(call.is_safe_navigation());
333 }
334
335 #[test]
336 fn integer_flags_test() {
337 let source = r"
3380b1
339";
340 let result = parse(source.as_ref());
341
342 let node = result.node();
343 let i = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
344 let i = i.as_integer_node().unwrap();
345 assert!(i.is_binary());
346 assert!(!i.is_decimal());
347 assert!(!i.is_octal());
348 assert!(!i.is_hexadecimal());
349
350 let source = r"
3511
352";
353 let result = parse(source.as_ref());
354
355 let node = result.node();
356 let i = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
357 let i = i.as_integer_node().unwrap();
358 assert!(!i.is_binary());
359 assert!(i.is_decimal());
360 assert!(!i.is_octal());
361 assert!(!i.is_hexadecimal());
362
363 let source = r"
3640o1
365";
366 let result = parse(source.as_ref());
367
368 let node = result.node();
369 let i = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
370 let i = i.as_integer_node().unwrap();
371 assert!(!i.is_binary());
372 assert!(!i.is_decimal());
373 assert!(i.is_octal());
374 assert!(!i.is_hexadecimal());
375
376 let source = r"
3770x1
378";
379 let result = parse(source.as_ref());
380
381 let node = result.node();
382 let i = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
383 let i = i.as_integer_node().unwrap();
384 assert!(!i.is_binary());
385 assert!(!i.is_decimal());
386 assert!(!i.is_octal());
387 assert!(i.is_hexadecimal());
388 }
389
390 #[test]
391 fn range_flags_test() {
392 let source = r"
3930..1
394";
395 let result = parse(source.as_ref());
396
397 let node = result.node();
398 let range = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
399 let range = range.as_range_node().unwrap();
400 assert!(!range.is_exclude_end());
401
402 let source = r"
4030...1
404";
405 let result = parse(source.as_ref());
406
407 let node = result.node();
408 let range = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
409 let range = range.as_range_node().unwrap();
410 assert!(range.is_exclude_end());
411 }
412
413 #[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
414 #[test]
415 fn regex_flags_test() {
416 let source = r"
417/a/i
418";
419 let result = parse(source.as_ref());
420
421 let node = result.node();
422 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
423 let regex = regex.as_regular_expression_node().unwrap();
424 assert!(regex.is_ignore_case());
425 assert!(!regex.is_extended());
426 assert!(!regex.is_multi_line());
427 assert!(!regex.is_euc_jp());
428 assert!(!regex.is_ascii_8bit());
429 assert!(!regex.is_windows_31j());
430 assert!(!regex.is_utf_8());
431 assert!(!regex.is_once());
432
433 let source = r"
434/a/x
435";
436 let result = parse(source.as_ref());
437
438 let node = result.node();
439 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
440 let regex = regex.as_regular_expression_node().unwrap();
441 assert!(!regex.is_ignore_case());
442 assert!(regex.is_extended());
443 assert!(!regex.is_multi_line());
444 assert!(!regex.is_euc_jp());
445 assert!(!regex.is_ascii_8bit());
446 assert!(!regex.is_windows_31j());
447 assert!(!regex.is_utf_8());
448 assert!(!regex.is_once());
449
450 let source = r"
451/a/m
452";
453 let result = parse(source.as_ref());
454
455 let node = result.node();
456 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
457 let regex = regex.as_regular_expression_node().unwrap();
458 assert!(!regex.is_ignore_case());
459 assert!(!regex.is_extended());
460 assert!(regex.is_multi_line());
461 assert!(!regex.is_euc_jp());
462 assert!(!regex.is_ascii_8bit());
463 assert!(!regex.is_windows_31j());
464 assert!(!regex.is_utf_8());
465 assert!(!regex.is_once());
466
467 let source = r"
468/a/e
469";
470 let result = parse(source.as_ref());
471
472 let node = result.node();
473 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
474 let regex = regex.as_regular_expression_node().unwrap();
475 assert!(!regex.is_ignore_case());
476 assert!(!regex.is_extended());
477 assert!(!regex.is_multi_line());
478 assert!(regex.is_euc_jp());
479 assert!(!regex.is_ascii_8bit());
480 assert!(!regex.is_windows_31j());
481 assert!(!regex.is_utf_8());
482 assert!(!regex.is_once());
483
484 let source = r"
485/a/n
486";
487 let result = parse(source.as_ref());
488
489 let node = result.node();
490 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
491 let regex = regex.as_regular_expression_node().unwrap();
492 assert!(!regex.is_ignore_case());
493 assert!(!regex.is_extended());
494 assert!(!regex.is_multi_line());
495 assert!(!regex.is_euc_jp());
496 assert!(regex.is_ascii_8bit());
497 assert!(!regex.is_windows_31j());
498 assert!(!regex.is_utf_8());
499 assert!(!regex.is_once());
500
501 let source = r"
502/a/s
503";
504 let result = parse(source.as_ref());
505
506 let node = result.node();
507 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
508 let regex = regex.as_regular_expression_node().unwrap();
509 assert!(!regex.is_ignore_case());
510 assert!(!regex.is_extended());
511 assert!(!regex.is_multi_line());
512 assert!(!regex.is_euc_jp());
513 assert!(!regex.is_ascii_8bit());
514 assert!(regex.is_windows_31j());
515 assert!(!regex.is_utf_8());
516 assert!(!regex.is_once());
517
518 let source = r"
519/a/u
520";
521 let result = parse(source.as_ref());
522
523 let node = result.node();
524 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
525 let regex = regex.as_regular_expression_node().unwrap();
526 assert!(!regex.is_ignore_case());
527 assert!(!regex.is_extended());
528 assert!(!regex.is_multi_line());
529 assert!(!regex.is_euc_jp());
530 assert!(!regex.is_ascii_8bit());
531 assert!(!regex.is_windows_31j());
532 assert!(regex.is_utf_8());
533 assert!(!regex.is_once());
534
535 let source = r"
536/a/o
537";
538 let result = parse(source.as_ref());
539
540 let node = result.node();
541 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
542 let regex = regex.as_regular_expression_node().unwrap();
543 assert!(!regex.is_ignore_case());
544 assert!(!regex.is_extended());
545 assert!(!regex.is_multi_line());
546 assert!(!regex.is_euc_jp());
547 assert!(!regex.is_ascii_8bit());
548 assert!(!regex.is_windows_31j());
549 assert!(!regex.is_utf_8());
550 assert!(regex.is_once());
551 }
552
553 #[test]
554 fn visitor_traversal_test() {
555 use crate::{Node, Visit};
556
557 #[derive(Default)]
558 struct NodeCounts {
559 pre_parent: usize,
560 post_parent: usize,
561 pre_leaf: usize,
562 post_leaf: usize,
563 }
564
565 #[derive(Default)]
566 struct CountingVisitor {
567 counts: NodeCounts,
568 }
569
570 impl Visit<'_> for CountingVisitor {
571 fn visit_branch_node_enter(&mut self, _node: Node<'_>) {
572 self.counts.pre_parent += 1;
573 }
574
575 fn visit_branch_node_leave(&mut self) {
576 self.counts.post_parent += 1;
577 }
578
579 fn visit_leaf_node_enter(&mut self, _node: Node<'_>) {
580 self.counts.pre_leaf += 1;
581 }
582
583 fn visit_leaf_node_leave(&mut self) {
584 self.counts.post_leaf += 1;
585 }
586 }
587
588 let source = r"
589module Example
590 x = call_func(3, 4)
591 y = x.call_func 5, 6
592end
593";
594 let result = parse(source.as_ref());
595 let node = result.node();
596 let mut visitor = CountingVisitor::default();
597 visitor.visit(&node);
598
599 assert_eq!(7, visitor.counts.pre_parent);
600 assert_eq!(7, visitor.counts.post_parent);
601 assert_eq!(6, visitor.counts.pre_leaf);
602 assert_eq!(6, visitor.counts.post_leaf);
603 }
604
605 #[test]
606 fn visitor_lifetime_test() {
607 use crate::{Node, Visit};
608
609 #[derive(Default)]
610 struct StackingNodeVisitor<'a> {
611 stack: Vec<Node<'a>>,
612 max_depth: usize,
613 }
614
615 impl<'pr> Visit<'pr> for StackingNodeVisitor<'pr> {
616 fn visit_branch_node_enter(&mut self, node: Node<'pr>) {
617 self.stack.push(node);
618 }
619
620 fn visit_branch_node_leave(&mut self) {
621 self.stack.pop();
622 }
623
624 fn visit_leaf_node_leave(&mut self) {
625 self.max_depth = self.max_depth.max(self.stack.len());
626 }
627 }
628
629 let source = r"
630module Example
631 x = call_func(3, 4)
632 y = x.call_func 5, 6
633end
634";
635 let result = parse(source.as_ref());
636 let node = result.node();
637 let mut visitor = StackingNodeVisitor::default();
638 visitor.visit(&node);
639
640 assert_eq!(0, visitor.stack.len());
641 assert_eq!(5, visitor.max_depth);
642 }
643
644 #[test]
645 fn integer_value_test() {
646 let result = parse("0xA".as_ref());
647 let integer = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap().as_integer_node().unwrap().value();
648 let value: i32 = integer.try_into().unwrap();
649
650 assert_eq!(value, 10);
651 }
652
653 #[test]
654 fn integer_small_value_to_u32_digits_test() {
655 let result = parse("0xA".as_ref());
656 let integer = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap().as_integer_node().unwrap().value();
657 let (negative, digits) = integer.to_u32_digits();
658
659 assert!(!negative);
660 assert_eq!(digits, &[10]);
661 }
662
663 #[test]
664 fn integer_large_value_to_u32_digits_test() {
665 let result = parse("0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".as_ref());
666 let integer = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap().as_integer_node().unwrap().value();
667 let (negative, digits) = integer.to_u32_digits();
668
669 assert!(!negative);
670 assert_eq!(digits, &[4_294_967_295, 4_294_967_295, 4_294_967_295, 2_147_483_647]);
671 }
672
673 #[test]
674 fn float_value_test() {
675 let result = parse("1.0".as_ref());
676 let value: f64 = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap().as_float_node().unwrap().value();
677
678 assert!((value - 1.0).abs() < f64::EPSILON);
679 }
680
681 #[test]
682 fn regex_value_test() {
683 let result = parse(b"//");
684 let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap().as_regular_expression_node().unwrap();
685 assert_eq!(node.unescaped(), b"");
686 }
687
688 #[test]
689 fn node_field_lifetime_test() {
690 #![allow(clippy::needless_pass_by_value)]
693
694 use crate::Node;
695
696 #[derive(Default)]
697 struct Extract<'pr> {
698 scopes: Vec<crate::ConstantId<'pr>>,
699 }
700
701 impl<'pr> Extract<'pr> {
702 fn push_scope(&mut self, path: Node<'pr>) {
703 if let Some(cread) = path.as_constant_read_node() {
704 self.scopes.push(cread.name());
705 } else if let Some(cpath) = path.as_constant_path_node() {
706 if let Some(parent) = cpath.parent() {
707 self.push_scope(parent);
708 }
709 self.scopes.push(cpath.name().unwrap());
710 } else {
711 panic!("Wrong node kind!");
712 }
713 }
714 }
715
716 let source = "Some::Random::Constant";
717 let result = parse(source.as_ref());
718 let node = result.node();
719 let mut extractor = Extract::default();
720 extractor.push_scope(node.as_program_node().unwrap().statements().body().iter().next().unwrap());
721 assert_eq!(3, extractor.scopes.len());
722 }
723
724 #[test]
725 fn malformed_shebang() {
726 let source = "#!\x00";
727 let result = parse(source.as_ref());
728 assert!(result.errors().next().is_none());
729 assert!(result.warnings().next().is_none());
730 }
731}