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 use std::ptr::NonNull;
12
13 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
16}
17
18mod node;
19mod node_ext;
20mod parse_result;
21
22use std::ffi::CString;
23use std::ptr::NonNull;
24
25pub use self::bindings::*;
26pub use self::node::{ConstantId, ConstantList, ConstantListIter, Integer, NodeList, NodeListIter};
27pub use self::node_ext::{ConstantPathError, FullName};
28pub use self::parse_result::{Comment, CommentType, Comments, Diagnostic, Diagnostics, Location, MagicComment, MagicComments, ParseResult};
29
30use ruby_prism_sys::{
31 pm_arena_new, pm_options_command_line_set, pm_options_encoding_locked_set, pm_options_encoding_set, pm_options_filepath_set, pm_options_free, pm_options_frozen_string_literal_set, pm_options_line_set, pm_options_main_script_set, pm_options_new, pm_options_partial_script_set,
32 pm_options_scope_forwarding_set, pm_options_scope_init, pm_options_scope_local_mut, pm_options_scope_mut, pm_options_scopes_init, pm_options_t, pm_options_version_set, pm_parse, pm_parser_new, pm_string_constant_init,
33};
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum Version {
38 Latest,
40 CRuby3_3,
42 CRuby3_4,
44 CRuby3_5,
46 CRuby4_1,
48}
49
50impl Version {
51 unsafe fn set_on(self, opts: *mut pm_options_t) {
54 match self {
55 Self::Latest => {
56 pm_options_version_set(opts, std::ptr::null(), 0);
57 },
58 Self::CRuby3_3 => {
59 pm_options_version_set(opts, c"3.3".as_ptr(), 3);
60 },
61 Self::CRuby3_4 => {
62 pm_options_version_set(opts, c"3.4".as_ptr(), 3);
63 },
64 Self::CRuby3_5 => {
65 pm_options_version_set(opts, c"3.5".as_ptr(), 3);
66 },
67 Self::CRuby4_1 => {
68 pm_options_version_set(opts, c"4.1".as_ptr(), 3);
69 },
70 }
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
76pub enum CommandLineFlag {
77 A,
79 E,
81 L,
83 N,
85 P,
87 X,
89}
90
91impl From<CommandLineFlag> for u8 {
92 fn from(flag: CommandLineFlag) -> Self {
93 match flag {
94 CommandLineFlag::A => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_A,
95 CommandLineFlag::E => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_E,
96 CommandLineFlag::L => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_L,
97 CommandLineFlag::N => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_N,
98 CommandLineFlag::P => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_P,
99 CommandLineFlag::X => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_X,
100 }
101 }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
106pub enum ScopeForwardingFlag {
107 Positionals,
109 Keywords,
111 Block,
113 All,
115}
116
117impl From<ScopeForwardingFlag> for u8 {
118 fn from(flag: ScopeForwardingFlag) -> Self {
119 match flag {
120 ScopeForwardingFlag::Positionals => ruby_prism_sys::PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS,
121 ScopeForwardingFlag::Keywords => ruby_prism_sys::PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS,
122 ScopeForwardingFlag::Block => ruby_prism_sys::PM_OPTIONS_SCOPE_FORWARDING_BLOCK,
123 ScopeForwardingFlag::All => ruby_prism_sys::PM_OPTIONS_SCOPE_FORWARDING_ALL,
124 }
125 }
126}
127
128#[derive(Debug, Clone, Default)]
130pub struct Scope {
131 locals: Vec<Vec<u8>>,
132 forwarding: Vec<ScopeForwardingFlag>,
133}
134
135impl Scope {
136 #[must_use]
138 pub fn locals(mut self, locals: Vec<Vec<u8>>) -> Self {
139 self.locals = locals;
140 self
141 }
142
143 #[must_use]
145 pub fn forwarding(mut self, forwarding: Vec<ScopeForwardingFlag>) -> Self {
146 self.forwarding = forwarding;
147 self
148 }
149}
150
151#[derive(Debug, Clone, Default)]
153pub struct Options {
154 filepath: Option<String>,
155 line: Option<i32>,
156 encoding: Option<String>,
157 encoding_locked: bool,
158 frozen_string_literal: Option<bool>,
159 command_line: Vec<CommandLineFlag>,
160 version: Option<Version>,
161 main_script: bool,
162 partial_script: bool,
163 scopes: Vec<Scope>,
164}
165
166impl Options {
167 #[must_use]
169 pub fn filepath(mut self, filepath: &str) -> Self {
170 self.filepath = Some(filepath.to_string());
171 self
172 }
173
174 #[must_use]
176 pub const fn line(mut self, line: i32) -> Self {
177 self.line = Some(line);
178 self
179 }
180
181 #[must_use]
183 pub fn encoding(mut self, encoding: &str) -> Self {
184 self.encoding = Some(encoding.to_string());
185 self
186 }
187
188 #[must_use]
190 pub const fn encoding_locked(mut self, locked: bool) -> Self {
191 self.encoding_locked = locked;
192 self
193 }
194
195 #[must_use]
199 pub const fn frozen_string_literal(mut self, frozen: Option<bool>) -> Self {
200 self.frozen_string_literal = frozen;
201 self
202 }
203
204 #[must_use]
206 pub fn command_line(mut self, command_line: Vec<CommandLineFlag>) -> Self {
207 self.command_line = command_line;
208 self
209 }
210
211 #[must_use]
213 pub const fn version(mut self, version: Version) -> Self {
214 self.version = Some(version);
215 self
216 }
217
218 #[must_use]
220 pub const fn main_script(mut self, main_script: bool) -> Self {
221 self.main_script = main_script;
222 self
223 }
224
225 #[must_use]
227 pub const fn partial_script(mut self, partial_script: bool) -> Self {
228 self.partial_script = partial_script;
229 self
230 }
231
232 #[must_use]
234 pub fn scope(mut self, scope: Scope) -> Self {
235 self.scopes.push(scope);
236 self
237 }
238
239 #[must_use]
241 pub fn scopes(mut self, scopes: Vec<Scope>) -> Self {
242 self.scopes = scopes;
243 self
244 }
245
246 #[must_use]
253 pub fn build(self) -> ParseOptions {
254 let opts = unsafe { pm_options_new() };
255
256 let c_filepath = self.filepath.map(|filepath| {
257 let cstring = CString::new(filepath).unwrap();
258 unsafe { pm_options_filepath_set(opts, cstring.as_ptr()) };
259 cstring
260 });
261
262 if let Some(line) = self.line {
263 unsafe { pm_options_line_set(opts, line) };
264 }
265
266 let c_encoding = self.encoding.map(|encoding| {
267 let cstring = CString::new(encoding).unwrap();
268 unsafe { pm_options_encoding_set(opts, cstring.as_ptr()) };
269 cstring
270 });
271
272 if self.encoding_locked {
273 unsafe { pm_options_encoding_locked_set(opts, true) };
274 }
275
276 if let Some(frozen) = self.frozen_string_literal {
277 unsafe { pm_options_frozen_string_literal_set(opts, frozen) };
278 }
279
280 let command_line = self.command_line.iter().fold(0u8, |acc, &flag| acc | u8::from(flag));
281 if command_line != 0 {
282 unsafe { pm_options_command_line_set(opts, command_line) };
283 }
284
285 if let Some(version) = self.version {
286 unsafe { version.set_on(opts) };
287 }
288
289 if self.main_script {
290 unsafe { pm_options_main_script_set(opts, true) };
291 }
292
293 if self.partial_script {
294 unsafe { pm_options_partial_script_set(opts, true) };
295 }
296
297 if !self.scopes.is_empty() {
298 unsafe { pm_options_scopes_init(opts, self.scopes.len()) };
299
300 for (scope_index, scope) in self.scopes.iter().enumerate() {
301 let pm_scope = unsafe { pm_options_scope_mut(opts, scope_index) };
302 unsafe { pm_options_scope_init(pm_scope, scope.locals.len()) };
303
304 for (local_index, local) in scope.locals.iter().enumerate() {
305 let pm_local = unsafe { pm_options_scope_local_mut(pm_scope, local_index) };
306 unsafe { pm_string_constant_init(pm_local, local.as_ptr().cast::<i8>(), local.len()) };
307 }
308
309 let forwarding = scope.forwarding.iter().fold(0u8, |acc, &flag| acc | u8::from(flag));
310 if forwarding != 0 {
311 unsafe { pm_options_scope_forwarding_set(pm_scope, forwarding) };
312 }
313 }
314 }
315
316 ParseOptions {
317 options: opts,
318 _filepath: c_filepath,
319 _encoding: c_encoding,
320 _scopes: self.scopes,
321 }
322 }
323}
324
325pub struct ParseOptions {
328 options: *mut pm_options_t,
329 _filepath: Option<CString>,
332 _encoding: Option<CString>,
333 _scopes: Vec<Scope>,
335}
336
337impl Drop for ParseOptions {
338 fn drop(&mut self) {
339 unsafe { pm_options_free(self.options) };
340 }
341}
342
343unsafe fn parse_impl(source: &[u8], options: *const pm_options_t) -> ParseResult<'_> {
349 let arena = pm_arena_new();
350 let parser = pm_parser_new(arena, source.as_ptr(), source.len(), options);
351 let node = NonNull::new_unchecked(pm_parse(parser));
352 ParseResult::new(source, arena, parser, node)
353}
354
355#[must_use]
362pub fn parse(source: &[u8]) -> ParseResult<'_> {
363 unsafe { parse_impl(source, std::ptr::null()) }
364}
365
366#[must_use]
374pub fn parse_with_options<'a>(source: &'a [u8], options: &'a ParseOptions) -> ParseResult<'a> {
375 unsafe { parse_impl(source, options.options) }
376}
377
378#[cfg(test)]
379mod tests {
380 use super::parse;
381
382 #[test]
383 fn comments_test() {
384 let source = "# comment 1\n# comment 2\n# comment 3\n";
385 let result = parse(source.as_ref());
386
387 for comment in result.comments() {
388 assert_eq!(super::CommentType::InlineComment, comment.type_());
389 let text = std::str::from_utf8(comment.text()).unwrap();
390 assert!(text.starts_with("# comment"));
391 }
392 }
393
394 #[test]
395 fn line_offsets_test() {
396 let source = "";
397 let result = parse(source.as_ref());
398
399 let expected: [u32; 1] = [0];
400 assert_eq!(expected, result.line_offsets());
401
402 let source = "1 + 1";
403 let result = parse(source.as_ref());
404
405 let expected: [u32; 1] = [0];
406 assert_eq!(expected, result.line_offsets());
407
408 let source = "begin\n1 + 1\n2 + 2\nend";
409 let result = parse(source.as_ref());
410
411 let expected: [u32; 4] = [0, 6, 12, 18];
412 assert_eq!(expected, result.line_offsets());
413 }
414
415 #[test]
416 fn magic_comments_test() {
417 use crate::MagicComment;
418
419 let source = "# typed: ignore\n# typed:true\n#typed: strict\n";
420 let result = parse(source.as_ref());
421
422 let comments: Vec<MagicComment<'_>> = result.magic_comments().collect();
423 assert_eq!(3, comments.len());
424
425 assert_eq!(b"typed", comments[0].key());
426 assert_eq!(b"ignore", comments[0].value());
427
428 assert_eq!(b"typed", comments[1].key());
429 assert_eq!(b"true", comments[1].value());
430
431 assert_eq!(b"typed", comments[2].key());
432 assert_eq!(b"strict", comments[2].value());
433 }
434
435 #[test]
436 fn data_loc_test() {
437 let source = "1";
438 let result = parse(source.as_ref());
439 let data_loc = result.data_loc();
440 assert!(data_loc.is_none());
441
442 let source = "__END__\nabc\n";
443 let result = parse(source.as_ref());
444 let data_loc = result.data_loc().unwrap();
445 let slice = std::str::from_utf8(result.as_slice(&data_loc)).unwrap();
446 assert_eq!(slice, "__END__\nabc\n");
447
448 let source = "1\n2\n3\n__END__\nabc\ndef\n";
449 let result = parse(source.as_ref());
450 let data_loc = result.data_loc().unwrap();
451 let slice = std::str::from_utf8(result.as_slice(&data_loc)).unwrap();
452 assert_eq!(slice, "__END__\nabc\ndef\n");
453 }
454
455 #[test]
456 fn location_slice_test() {
457 let source = "111 + 222 + 333";
458 let result = parse(source.as_ref());
459
460 let node = result.node();
461 let node = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
462 let node = node.as_call_node().unwrap().receiver().unwrap();
463 let plus = node.as_call_node().unwrap();
464 let node = plus.arguments().unwrap().arguments().iter().next().unwrap();
465
466 let location = node.as_integer_node().unwrap().location();
467 let slice = std::str::from_utf8(result.as_slice(&location)).unwrap();
468
469 assert_eq!(slice, "222");
470 assert_eq!(6, location.start);
471 assert_eq!(location.start, location.start());
472 assert_eq!(9, location.end());
473
474 let recv_loc = plus.receiver().unwrap().location();
475 assert_eq!(recv_loc.as_slice(), b"111");
476 assert_eq!(0, recv_loc.start);
477 assert_eq!(3, recv_loc.end());
478
479 let joined = recv_loc.join(&location).unwrap();
480 assert_eq!(joined.as_slice(), b"111 + 222");
481
482 let not_joined = location.join(&recv_loc);
483 assert!(not_joined.is_none());
484
485 {
486 let result = parse(source.as_ref());
487 let node = result.node();
488 let node = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
489 let node = node.as_call_node().unwrap().receiver().unwrap();
490 let plus = node.as_call_node().unwrap();
491 let node = plus.arguments().unwrap().arguments().iter().next().unwrap();
492
493 let location = node.as_integer_node().unwrap().location();
494 let not_joined = recv_loc.join(&location);
495 assert!(not_joined.is_none());
496
497 let not_joined = location.join(&recv_loc);
498 assert!(not_joined.is_none());
499 }
500
501 let location = node.location();
502 let slice = std::str::from_utf8(result.as_slice(&location)).unwrap();
503
504 assert_eq!(slice, "222");
505
506 let slice = std::str::from_utf8(location.as_slice()).unwrap();
507
508 assert_eq!(slice, "222");
509 }
510
511 #[test]
512 fn location_line_column_test() {
513 let source = "first\nsecond\nthird";
514 let result = parse(source.as_ref());
515
516 let node = result.node();
517 let program = node.as_program_node().unwrap();
518 let statements = program.statements().body();
519 let mut iter = statements.iter();
520
521 let _first = iter.next().unwrap();
522 let second = iter.next().unwrap();
523 let third = iter.next().unwrap();
524
525 let second_loc = second.location();
526 assert_eq!(second_loc.start_line(), 2);
527 assert_eq!(second_loc.end_line(), 2);
528 assert_eq!(second_loc.start_column(), 0);
529 assert_eq!(second_loc.end_column(), 6);
530
531 let third_loc = third.location();
532 assert_eq!(third_loc.start_line(), 3);
533 assert_eq!(third_loc.end_line(), 3);
534 assert_eq!(third_loc.start_column(), 0);
535 assert_eq!(third_loc.end_column(), 5);
536 }
537
538 #[test]
539 fn location_chop_test() {
540 let result = parse(b"foo");
541 let mut location = result.node().as_program_node().unwrap().location();
542
543 assert_eq!(location.chop().as_slice(), b"fo");
544 assert_eq!(location.chop().chop().chop().as_slice(), b"");
545
546 for _ in 0..10 {
548 location = location.chop();
549 }
550 assert_eq!(location.as_slice(), b"");
551 }
552
553 #[test]
554 fn visitor_test() {
555 use super::{visit_interpolated_regular_expression_node, visit_regular_expression_node, InterpolatedRegularExpressionNode, RegularExpressionNode, Visit};
556
557 struct RegularExpressionVisitor {
558 count: usize,
559 }
560
561 impl Visit<'_> for RegularExpressionVisitor {
562 fn visit_interpolated_regular_expression_node(&mut self, node: &InterpolatedRegularExpressionNode<'_>) {
563 self.count += 1;
564 visit_interpolated_regular_expression_node(self, node);
565 }
566
567 fn visit_regular_expression_node(&mut self, node: &RegularExpressionNode<'_>) {
568 self.count += 1;
569 visit_regular_expression_node(self, node);
570 }
571 }
572
573 let source = "# comment 1\n# comment 2\nmodule Foo; class Bar; /abc #{/def/}/; end; end";
574 let result = parse(source.as_ref());
575
576 let mut visitor = RegularExpressionVisitor { count: 0 };
577 visitor.visit(&result.node());
578
579 assert_eq!(visitor.count, 2);
580 }
581
582 #[test]
583 fn node_upcast_test() {
584 use super::Node;
585
586 let source = "module Foo; end";
587 let result = parse(source.as_ref());
588
589 let node = result.node();
590 let upcast_node = node.as_program_node().unwrap().as_node();
591 assert!(matches!(upcast_node, Node::ProgramNode { .. }));
592
593 let node = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
594 let upcast_node = node.as_module_node().unwrap().as_node();
595 assert!(matches!(upcast_node, Node::ModuleNode { .. }));
596 }
597
598 #[test]
599 fn constant_id_test() {
600 let source = "module Foo; x = 1; y = 2; end";
601 let result = parse(source.as_ref());
602
603 let node = result.node();
604 assert_eq!(node.as_program_node().unwrap().statements().body().len(), 1);
605 assert!(!node.as_program_node().unwrap().statements().body().is_empty());
606 let module = node.as_program_node().and_then(|pn| pn.statements().body().first()).unwrap();
607 let module = module.as_module_node().unwrap();
608
609 assert_eq!(module.locals().len(), 2);
610 assert!(!module.locals().is_empty());
611
612 assert_eq!(module.locals().first().unwrap().as_slice(), b"x");
613 assert_eq!(module.locals().last().unwrap().as_slice(), b"y");
614
615 let source = "module Foo; end";
616 let result = parse(source.as_ref());
617
618 let node = result.node();
619 assert_eq!(node.as_program_node().unwrap().statements().body().len(), 1);
620 assert!(!node.as_program_node().unwrap().statements().body().is_empty());
621 let module = node.as_program_node().and_then(|pn| pn.statements().body().first()).unwrap();
622 let module = module.as_module_node().unwrap();
623
624 assert_eq!(module.locals().len(), 0);
625 assert!(module.locals().is_empty());
626 }
627
628 #[test]
629 fn optional_loc_test() {
630 let source = r"
631module Example
632 x = call_func(3, 4)
633 y = x.call_func 5, 6
634end
635";
636 let result = parse(source.as_ref());
637
638 let node = result.node();
639 let module = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
640 let module = module.as_module_node().unwrap();
641 let body = module.body();
642 let writes = body.iter().next().unwrap().as_statements_node().unwrap().body().iter().collect::<Vec<_>>();
643 assert_eq!(writes.len(), 2);
644
645 let asgn = &writes[0];
646 let call = asgn.as_local_variable_write_node().unwrap().value();
647 let call = call.as_call_node().unwrap();
648
649 let call_operator_loc = call.call_operator_loc();
650 assert!(call_operator_loc.is_none());
651 let closing_loc = call.closing_loc();
652 assert!(closing_loc.is_some());
653
654 let asgn = &writes[1];
655 let call = asgn.as_local_variable_write_node().unwrap().value();
656 let call = call.as_call_node().unwrap();
657
658 let call_operator_loc = call.call_operator_loc();
659 assert!(call_operator_loc.is_some());
660 let closing_loc = call.closing_loc();
661 assert!(closing_loc.is_none());
662 }
663
664 #[test]
665 fn frozen_strings_test() {
666 let source = r#"
667# frozen_string_literal: true
668"foo"
669"#;
670 let result = parse(source.as_ref());
671 assert!(result.frozen_string_literals());
672
673 let source = "3";
674 let result = parse(source.as_ref());
675 assert!(!result.frozen_string_literals());
676 }
677
678 #[test]
679 fn string_flags_test() {
680 let source = r#"
681# frozen_string_literal: true
682"foo"
683"#;
684 let result = parse(source.as_ref());
685
686 let node = result.node();
687 let string = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
688 let string = string.as_string_node().unwrap();
689 assert!(string.is_frozen());
690
691 let source = r#"
692"foo"
693"#;
694 let result = parse(source.as_ref());
695
696 let node = result.node();
697 let string = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
698 let string = string.as_string_node().unwrap();
699 assert!(!string.is_frozen());
700 }
701
702 #[test]
703 fn call_flags_test() {
704 let source = r"
705x
706";
707 let result = parse(source.as_ref());
708
709 let node = result.node();
710 let call = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
711 let call = call.as_call_node().unwrap();
712 assert!(call.is_variable_call());
713
714 let source = r"
715x&.foo
716";
717 let result = parse(source.as_ref());
718
719 let node = result.node();
720 let call = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
721 let call = call.as_call_node().unwrap();
722 assert!(call.is_safe_navigation());
723 }
724
725 #[test]
726 fn integer_flags_test() {
727 let source = r"
7280b1
729";
730 let result = parse(source.as_ref());
731
732 let node = result.node();
733 let i = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
734 let i = i.as_integer_node().unwrap();
735 assert!(i.is_binary());
736 assert!(!i.is_decimal());
737 assert!(!i.is_octal());
738 assert!(!i.is_hexadecimal());
739
740 let source = r"
7411
742";
743 let result = parse(source.as_ref());
744
745 let node = result.node();
746 let i = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
747 let i = i.as_integer_node().unwrap();
748 assert!(!i.is_binary());
749 assert!(i.is_decimal());
750 assert!(!i.is_octal());
751 assert!(!i.is_hexadecimal());
752
753 let source = r"
7540o1
755";
756 let result = parse(source.as_ref());
757
758 let node = result.node();
759 let i = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
760 let i = i.as_integer_node().unwrap();
761 assert!(!i.is_binary());
762 assert!(!i.is_decimal());
763 assert!(i.is_octal());
764 assert!(!i.is_hexadecimal());
765
766 let source = r"
7670x1
768";
769 let result = parse(source.as_ref());
770
771 let node = result.node();
772 let i = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
773 let i = i.as_integer_node().unwrap();
774 assert!(!i.is_binary());
775 assert!(!i.is_decimal());
776 assert!(!i.is_octal());
777 assert!(i.is_hexadecimal());
778 }
779
780 #[test]
781 fn range_flags_test() {
782 let source = r"
7830..1
784";
785 let result = parse(source.as_ref());
786
787 let node = result.node();
788 let range = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
789 let range = range.as_range_node().unwrap();
790 assert!(!range.is_exclude_end());
791
792 let source = r"
7930...1
794";
795 let result = parse(source.as_ref());
796
797 let node = result.node();
798 let range = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
799 let range = range.as_range_node().unwrap();
800 assert!(range.is_exclude_end());
801 }
802
803 #[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
804 #[test]
805 fn regex_flags_test() {
806 let source = r"
807/a/i
808";
809 let result = parse(source.as_ref());
810
811 let node = result.node();
812 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
813 let regex = regex.as_regular_expression_node().unwrap();
814 assert!(regex.is_ignore_case());
815 assert!(!regex.is_extended());
816 assert!(!regex.is_multi_line());
817 assert!(!regex.is_euc_jp());
818 assert!(!regex.is_ascii_8bit());
819 assert!(!regex.is_windows_31j());
820 assert!(!regex.is_utf_8());
821 assert!(!regex.is_once());
822
823 let source = r"
824/a/x
825";
826 let result = parse(source.as_ref());
827
828 let node = result.node();
829 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
830 let regex = regex.as_regular_expression_node().unwrap();
831 assert!(!regex.is_ignore_case());
832 assert!(regex.is_extended());
833 assert!(!regex.is_multi_line());
834 assert!(!regex.is_euc_jp());
835 assert!(!regex.is_ascii_8bit());
836 assert!(!regex.is_windows_31j());
837 assert!(!regex.is_utf_8());
838 assert!(!regex.is_once());
839
840 let source = r"
841/a/m
842";
843 let result = parse(source.as_ref());
844
845 let node = result.node();
846 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
847 let regex = regex.as_regular_expression_node().unwrap();
848 assert!(!regex.is_ignore_case());
849 assert!(!regex.is_extended());
850 assert!(regex.is_multi_line());
851 assert!(!regex.is_euc_jp());
852 assert!(!regex.is_ascii_8bit());
853 assert!(!regex.is_windows_31j());
854 assert!(!regex.is_utf_8());
855 assert!(!regex.is_once());
856
857 let source = r"
858/a/e
859";
860 let result = parse(source.as_ref());
861
862 let node = result.node();
863 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
864 let regex = regex.as_regular_expression_node().unwrap();
865 assert!(!regex.is_ignore_case());
866 assert!(!regex.is_extended());
867 assert!(!regex.is_multi_line());
868 assert!(regex.is_euc_jp());
869 assert!(!regex.is_ascii_8bit());
870 assert!(!regex.is_windows_31j());
871 assert!(!regex.is_utf_8());
872 assert!(!regex.is_once());
873
874 let source = r"
875/a/n
876";
877 let result = parse(source.as_ref());
878
879 let node = result.node();
880 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
881 let regex = regex.as_regular_expression_node().unwrap();
882 assert!(!regex.is_ignore_case());
883 assert!(!regex.is_extended());
884 assert!(!regex.is_multi_line());
885 assert!(!regex.is_euc_jp());
886 assert!(regex.is_ascii_8bit());
887 assert!(!regex.is_windows_31j());
888 assert!(!regex.is_utf_8());
889 assert!(!regex.is_once());
890
891 let source = r"
892/a/s
893";
894 let result = parse(source.as_ref());
895
896 let node = result.node();
897 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
898 let regex = regex.as_regular_expression_node().unwrap();
899 assert!(!regex.is_ignore_case());
900 assert!(!regex.is_extended());
901 assert!(!regex.is_multi_line());
902 assert!(!regex.is_euc_jp());
903 assert!(!regex.is_ascii_8bit());
904 assert!(regex.is_windows_31j());
905 assert!(!regex.is_utf_8());
906 assert!(!regex.is_once());
907
908 let source = r"
909/a/u
910";
911 let result = parse(source.as_ref());
912
913 let node = result.node();
914 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
915 let regex = regex.as_regular_expression_node().unwrap();
916 assert!(!regex.is_ignore_case());
917 assert!(!regex.is_extended());
918 assert!(!regex.is_multi_line());
919 assert!(!regex.is_euc_jp());
920 assert!(!regex.is_ascii_8bit());
921 assert!(!regex.is_windows_31j());
922 assert!(regex.is_utf_8());
923 assert!(!regex.is_once());
924
925 let source = r"
926/a/o
927";
928 let result = parse(source.as_ref());
929
930 let node = result.node();
931 let regex = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
932 let regex = regex.as_regular_expression_node().unwrap();
933 assert!(!regex.is_ignore_case());
934 assert!(!regex.is_extended());
935 assert!(!regex.is_multi_line());
936 assert!(!regex.is_euc_jp());
937 assert!(!regex.is_ascii_8bit());
938 assert!(!regex.is_windows_31j());
939 assert!(!regex.is_utf_8());
940 assert!(regex.is_once());
941 }
942
943 #[test]
944 fn visitor_traversal_test() {
945 use crate::{Node, Visit};
946
947 #[derive(Default)]
948 struct NodeCounts {
949 pre_parent: usize,
950 post_parent: usize,
951 pre_leaf: usize,
952 post_leaf: usize,
953 }
954
955 #[derive(Default)]
956 struct CountingVisitor {
957 counts: NodeCounts,
958 }
959
960 impl Visit<'_> for CountingVisitor {
961 fn visit_branch_node_enter(&mut self, _node: Node<'_>) {
962 self.counts.pre_parent += 1;
963 }
964
965 fn visit_branch_node_leave(&mut self) {
966 self.counts.post_parent += 1;
967 }
968
969 fn visit_leaf_node_enter(&mut self, _node: Node<'_>) {
970 self.counts.pre_leaf += 1;
971 }
972
973 fn visit_leaf_node_leave(&mut self) {
974 self.counts.post_leaf += 1;
975 }
976 }
977
978 let source = r"
979module Example
980 x = call_func(3, 4)
981 y = x.call_func 5, 6
982end
983";
984 let result = parse(source.as_ref());
985 let node = result.node();
986 let mut visitor = CountingVisitor::default();
987 visitor.visit(&node);
988
989 assert_eq!(7, visitor.counts.pre_parent);
990 assert_eq!(7, visitor.counts.post_parent);
991 assert_eq!(6, visitor.counts.pre_leaf);
992 assert_eq!(6, visitor.counts.post_leaf);
993 }
994
995 #[test]
996 fn visitor_lifetime_test() {
997 use crate::{Node, Visit};
998
999 #[derive(Default)]
1000 struct StackingNodeVisitor<'a> {
1001 stack: Vec<Node<'a>>,
1002 max_depth: usize,
1003 }
1004
1005 impl<'pr> Visit<'pr> for StackingNodeVisitor<'pr> {
1006 fn visit_branch_node_enter(&mut self, node: Node<'pr>) {
1007 self.stack.push(node);
1008 }
1009
1010 fn visit_branch_node_leave(&mut self) {
1011 self.stack.pop();
1012 }
1013
1014 fn visit_leaf_node_leave(&mut self) {
1015 self.max_depth = self.max_depth.max(self.stack.len());
1016 }
1017 }
1018
1019 let source = r"
1020module Example
1021 x = call_func(3, 4)
1022 y = x.call_func 5, 6
1023end
1024";
1025 let result = parse(source.as_ref());
1026 let node = result.node();
1027 let mut visitor = StackingNodeVisitor::default();
1028 visitor.visit(&node);
1029
1030 assert_eq!(0, visitor.stack.len());
1031 assert_eq!(5, visitor.max_depth);
1032 }
1033
1034 #[test]
1035 fn integer_value_test() {
1036 let result = parse("0xA".as_ref());
1037 let integer = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap().as_integer_node().unwrap().value();
1038 let value: i32 = integer.try_into().unwrap();
1039
1040 assert_eq!(value, 10);
1041 }
1042
1043 #[test]
1044 fn integer_small_value_to_u32_digits_test() {
1045 let result = parse("0xA".as_ref());
1046 let integer = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap().as_integer_node().unwrap().value();
1047 let (negative, digits) = integer.to_u32_digits();
1048
1049 assert!(!negative);
1050 assert_eq!(digits, &[10]);
1051 }
1052
1053 #[test]
1054 fn integer_large_value_to_u32_digits_test() {
1055 let result = parse("0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".as_ref());
1056 let integer = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap().as_integer_node().unwrap().value();
1057 let (negative, digits) = integer.to_u32_digits();
1058
1059 assert!(!negative);
1060 assert_eq!(digits, &[4_294_967_295, 4_294_967_295, 4_294_967_295, 2_147_483_647]);
1061 }
1062
1063 #[test]
1064 fn float_value_test() {
1065 let result = parse("1.0".as_ref());
1066 let value: f64 = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap().as_float_node().unwrap().value();
1067
1068 assert!((value - 1.0).abs() < f64::EPSILON);
1069 }
1070
1071 #[test]
1072 fn regex_value_test() {
1073 let result = parse(b"//");
1074 let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap().as_regular_expression_node().unwrap();
1075 assert_eq!(node.unescaped(), b"");
1076 }
1077
1078 #[test]
1079 fn node_field_lifetime_test() {
1080 #![allow(clippy::needless_pass_by_value)]
1083
1084 use crate::Node;
1085
1086 #[derive(Default)]
1087 struct Extract<'pr> {
1088 scopes: Vec<crate::ConstantId<'pr>>,
1089 }
1090
1091 impl<'pr> Extract<'pr> {
1092 fn push_scope(&mut self, path: Node<'pr>) {
1093 if let Some(cread) = path.as_constant_read_node() {
1094 self.scopes.push(cread.name());
1095 } else if let Some(cpath) = path.as_constant_path_node() {
1096 if let Some(parent) = cpath.parent() {
1097 self.push_scope(parent);
1098 }
1099 self.scopes.push(cpath.name().unwrap());
1100 } else {
1101 panic!("Wrong node kind!");
1102 }
1103 }
1104 }
1105
1106 let source = "Some::Random::Constant";
1107 let result = parse(source.as_ref());
1108 let node = result.node();
1109 let mut extractor = Extract::default();
1110 extractor.push_scope(node.as_program_node().unwrap().statements().body().iter().next().unwrap());
1111 assert_eq!(3, extractor.scopes.len());
1112 }
1113
1114 #[test]
1115 fn parse_with_options_frozen_string_literal_test() {
1116 use super::{parse_with_options, Options};
1117
1118 let source = b"\"foo\"";
1119 let options = Options::default().frozen_string_literal(Some(true)).build();
1120 let result = parse_with_options(source, &options);
1121
1122 let node = result.node();
1123 let string = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
1124 let string = string.as_string_node().unwrap();
1125 assert!(string.is_frozen());
1126 }
1127
1128 #[test]
1129 fn parse_with_options_filepath_test() {
1130 use super::{parse_with_options, Options};
1131
1132 let source = b"__FILE__";
1133 let options = Options::default().filepath("test.rb").build();
1134 let result = parse_with_options(source, &options);
1135 assert!(result.errors().next().is_none());
1136 }
1137
1138 #[test]
1139 fn parse_with_options_line_test() {
1140 use super::{parse_with_options, Options};
1141
1142 let source = b"1 + 2";
1143 let options = Options::default().line(10).build();
1144 let result = parse_with_options(source, &options);
1145 assert!(result.errors().next().is_none());
1146 }
1147
1148 #[test]
1149 fn parse_with_options_scopes_test() {
1150 use super::{parse_with_options, Options, Scope};
1151
1152 let source = b"x";
1153 let scope = Scope::default().locals(vec![b"x".to_vec()]);
1154 let options = Options::default().scope(scope).build();
1155 let result = parse_with_options(source, &options);
1156 assert!(result.errors().next().is_none());
1157
1158 let node = result.node();
1159 let stmt = node.as_program_node().unwrap().statements().body().iter().next().unwrap();
1160 assert!(stmt.as_local_variable_read_node().is_some());
1161 }
1162
1163 #[test]
1164 fn malformed_shebang() {
1165 let source = "#!\x00";
1166 let result = parse(source.as_ref());
1167 assert!(result.errors().next().is_none());
1168 assert!(result.warnings().next().is_none());
1169 }
1170}