ruby_prism/
lib.rs

1//! # ruby-prism
2//!
3//! Rustified version of Ruby's prism parser.
4//!
5#![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// Most of the code in this file is generated, so sometimes it generates code
8// that doesn't follow the clippy rules. We don't want to see those warnings.
9#[allow(clippy::too_many_lines, clippy::use_self)]
10mod bindings {
11    // In `build.rs`, we generate bindings based on the config.yml file. Here is
12    // where we pull in those bindings and make them part of our library.
13    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/// Parses the given source string and returns a parse result.
29///
30/// # Panics
31///
32/// Panics if the parser fails to initialize.
33///
34#[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        // The code below wouldn't typecheck prior to https://github.com/ruby/prism/pull/2519,
691        // but we need to stop clippy from complaining about it.
692        #![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}