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