sway_parse/item/
mod.rs

1use crate::{Parse, ParseResult, ParseToEnd, Parser, ParserConsumed};
2
3use sway_ast::keywords::{
4    AbiToken, ClassToken, ColonToken, ConfigurableToken, ConstToken, EnumToken, FnToken, ImplToken,
5    ModToken, MutToken, OpenAngleBracketToken, RefToken, SelfToken, SemicolonToken, StorageToken,
6    StructToken, TraitToken, TypeToken, UseToken, WhereToken,
7};
8use sway_ast::{
9    FnArg, FnArgs, FnSignature, ItemConst, ItemEnum, ItemFn, ItemKind, ItemStruct, ItemTrait,
10    ItemTypeAlias, ItemUse, Submodule, TraitType, TypeField,
11};
12use sway_error::parser_error::ParseErrorKind;
13
14mod item_abi;
15mod item_configurable;
16mod item_const;
17mod item_enum;
18mod item_fn;
19mod item_impl;
20mod item_storage;
21mod item_struct;
22mod item_trait;
23mod item_type_alias;
24mod item_use;
25
26impl Parse for ItemKind {
27    fn parse(parser: &mut Parser) -> ParseResult<ItemKind> {
28        // FIXME(Centril): Visibility should be moved out of `ItemKind` variants,
29        // introducing a struct `Item` that holds the visibility and the kind,
30        // and then validate in an "AST validation" step which kinds that should have `pub`s.
31
32        let mut visibility = parser.take();
33
34        let kind = if let Some(mut item) = parser.guarded_parse::<ModToken, Submodule>()? {
35            item.visibility = visibility.take();
36            ItemKind::Submodule(item)
37        } else if let Some(mut item) = parser.guarded_parse::<UseToken, ItemUse>()? {
38            item.visibility = visibility.take();
39            ItemKind::Use(item)
40        } else if let Some(mut item) = parser.guarded_parse::<ClassToken, ItemStruct>()? {
41            item.visibility = visibility.take();
42            ItemKind::Struct(item)
43        } else if let Some(mut item) = parser.guarded_parse::<StructToken, ItemStruct>()? {
44            item.visibility = visibility.take();
45            ItemKind::Struct(item)
46        } else if let Some(mut item) = parser.guarded_parse::<EnumToken, ItemEnum>()? {
47            item.visibility = visibility.take();
48            ItemKind::Enum(item)
49        } else if let Some(mut item) = parser.guarded_parse::<FnToken, ItemFn>()? {
50            item.fn_signature.visibility = visibility.take();
51            ItemKind::Fn(item)
52        } else if let Some(mut item) = parser.guarded_parse::<TraitToken, ItemTrait>()? {
53            item.visibility = visibility.take();
54            ItemKind::Trait(item)
55        } else if let Some(item) = parser.guarded_parse::<ImplToken, _>()? {
56            ItemKind::Impl(item)
57        } else if let Some(item) = parser.guarded_parse::<AbiToken, _>()? {
58            ItemKind::Abi(item)
59        } else if let Some(mut item) = parser.guarded_parse::<ConstToken, ItemConst>()? {
60            item.visibility = visibility.take();
61            parser.take::<SemicolonToken>().ok_or_else(|| {
62                parser.emit_error(ParseErrorKind::ExpectedPunct {
63                    kinds: vec![sway_types::ast::PunctKind::Semicolon],
64                })
65            })?;
66            ItemKind::Const(item)
67        } else if let Some(item) = parser.guarded_parse::<StorageToken, _>()? {
68            ItemKind::Storage(item)
69        } else if let Some(item) = parser.guarded_parse::<ConfigurableToken, _>()? {
70            ItemKind::Configurable(item)
71        } else if let Some(mut item) = parser.guarded_parse::<TypeToken, ItemTypeAlias>()? {
72            item.visibility = visibility.take();
73            ItemKind::TypeAlias(item)
74        } else {
75            return Err(parser.emit_error(ParseErrorKind::ExpectedAnItem));
76        };
77
78        // Ban visibility qualifiers that haven't been consumed, but do so with recovery.
79        let _ = parser.ban_visibility_qualifier(&visibility);
80
81        Ok(kind)
82    }
83
84    fn error(
85        spans: Box<[sway_types::Span]>,
86        error: sway_error::handler::ErrorEmitted,
87    ) -> Option<Self>
88    where
89        Self: Sized,
90    {
91        Some(ItemKind::Error(spans, error))
92    }
93}
94
95impl Parse for TypeField {
96    fn parse(parser: &mut Parser) -> ParseResult<TypeField> {
97        let visibility = parser.take();
98        Ok(TypeField {
99            visibility,
100            name: parser.parse()?,
101            colon_token: if parser.peek::<ColonToken>().is_some() {
102                parser.parse()
103            } else {
104                Err(parser.emit_error(ParseErrorKind::MissingColonInEnumTypeField))
105            }?,
106            ty: parser.parse()?,
107        })
108    }
109}
110
111impl ParseToEnd for FnArgs {
112    fn parse_to_end<'a, 'e>(
113        mut parser: Parser<'a, '_>,
114    ) -> ParseResult<(FnArgs, ParserConsumed<'a>)> {
115        let mut ref_self: Option<RefToken> = None;
116        let mut mutable_self: Option<MutToken> = None;
117        if parser.peek::<(MutToken, SelfToken)>().is_some()
118            || parser.peek::<(RefToken, MutToken, SelfToken)>().is_some()
119        {
120            ref_self = parser.take();
121            mutable_self = parser.take();
122        }
123        match parser.take() {
124            Some(self_token) => {
125                match parser.take() {
126                    Some(comma_token) => {
127                        let (args, consumed) = parser.parse_to_end()?;
128                        let fn_args = FnArgs::NonStatic {
129                            self_token,
130                            ref_self,
131                            mutable_self,
132                            args_opt: Some((comma_token, args)),
133                        };
134                        Ok((fn_args, consumed))
135                    }
136                    None => {
137                        let fn_args = FnArgs::NonStatic {
138                            self_token,
139                            ref_self,
140                            mutable_self,
141                            args_opt: None,
142                        };
143                        match parser.check_empty() {
144                            Some(consumed) => Ok((fn_args, consumed)),
145                            None => Err(parser
146                                .emit_error(ParseErrorKind::ExpectedCommaOrCloseParenInFnArgs)),
147                        }
148                    }
149                }
150            }
151            None => {
152                let (args, consumed) = parser.parse_to_end()?;
153                let fn_args = FnArgs::Static(args);
154                Ok((fn_args, consumed))
155            }
156        }
157    }
158}
159
160impl Parse for FnArg {
161    fn parse(parser: &mut Parser) -> ParseResult<FnArg> {
162        Ok(FnArg {
163            pattern: parser.parse()?,
164            colon_token: parser.parse()?,
165            ty: parser.parse()?,
166        })
167    }
168}
169
170impl Parse for FnSignature {
171    fn parse(parser: &mut Parser) -> ParseResult<FnSignature> {
172        Ok(FnSignature {
173            visibility: parser.take(),
174            fn_token: parser.parse()?,
175            name: parser.parse()?,
176            generics: parser.guarded_parse::<OpenAngleBracketToken, _>()?,
177            arguments: parser.parse()?,
178            return_type_opt: match parser.take() {
179                Some(right_arrow_token) => {
180                    let ty = parser.parse()?;
181                    Some((right_arrow_token, ty))
182                }
183                None => None,
184            },
185            where_clause_opt: parser.guarded_parse::<WhereToken, _>()?,
186        })
187    }
188}
189
190impl Parse for TraitType {
191    fn parse(parser: &mut Parser) -> ParseResult<TraitType> {
192        let type_token = parser.parse()?;
193        let name = parser.parse()?;
194        let eq_token_opt = parser.take();
195        let ty_opt = match &eq_token_opt {
196            Some(_eq) => Some(parser.parse()?),
197            None => None,
198        };
199        let semicolon_token = parser.peek().unwrap_or_default();
200        Ok(TraitType {
201            type_token,
202            name,
203            eq_token_opt,
204            ty_opt,
205            semicolon_token,
206        })
207    }
208}
209
210// -------------------------------------------------------------------------------------------------
211
212#[cfg(test)]
213mod tests {
214    use super::*;
215    use crate::test_utils::parse;
216    use sway_ast::{AttributeDecl, Item, ItemTraitItem};
217
218    // Attribute name and its list of parameters
219    type ParameterizedAttr<'a> = (&'a str, Option<Vec<&'a str>>);
220
221    fn attributes(attributes: &[AttributeDecl]) -> Vec<Vec<ParameterizedAttr>> {
222        attributes
223            .iter()
224            .map(|attr_decl| {
225                attr_decl
226                    .attribute
227                    .get()
228                    .into_iter()
229                    .map(|att| {
230                        (
231                            att.name.as_str(),
232                            att.args.as_ref().map(|arg| {
233                                arg.get().into_iter().map(|a| a.name.as_str()).collect()
234                            }),
235                        )
236                    })
237                    .collect()
238            })
239            .collect()
240    }
241
242    #[test]
243    fn parse_doc_comment() {
244        let item = parse::<Item>(
245            r#"
246            // I will be ignored.
247            //! This is a misplaced inner doc comment.
248            /// This is an outer doc comment.
249            //! This is a misplaced inner doc comment.
250            // I will be ignored.
251            /// This is an outer doc comment.
252            // I will be ignored.
253            fn f() -> bool {
254                false
255            }
256            "#,
257        );
258        assert!(matches!(item.value, ItemKind::Fn(_)));
259        assert_eq!(
260            attributes(&item.attributes),
261            vec![
262                [(
263                    "doc-comment",
264                    Some(vec![" This is a misplaced inner doc comment."])
265                )],
266                [("doc-comment", Some(vec![" This is an outer doc comment."]))],
267                [(
268                    "doc-comment",
269                    Some(vec![" This is a misplaced inner doc comment."])
270                )],
271                [("doc-comment", Some(vec![" This is an outer doc comment."]))],
272            ]
273        );
274    }
275
276    #[test]
277    fn parse_doc_comment_struct() {
278        let item = parse::<Item>(
279            r#"
280            // I will be ignored.
281            //! This is a misplaced inner doc comment.
282            /// This is an outer doc comment.
283            //! This is a misplaced inner doc comment.
284            // I will be ignored.
285            /// This is an outer doc comment.
286            // I will be ignored.
287            struct MyStruct {
288                // I will be ignored.
289                //! This is a misplaced inner doc comment.
290                /// This is an outer doc comment.
291                //! This is a misplaced inner doc comment.
292                // I will be ignored.
293                /// This is an outer doc comment.
294                // I will be ignored.
295                a: bool,
296            }
297            "#,
298        );
299
300        /* struct annotations */
301        assert!(matches!(item.value, ItemKind::Struct(_)));
302        assert_eq!(
303            attributes(&item.attributes),
304            vec![
305                [(
306                    "doc-comment",
307                    Some(vec![" This is a misplaced inner doc comment."])
308                )],
309                [("doc-comment", Some(vec![" This is an outer doc comment."]))],
310                [(
311                    "doc-comment",
312                    Some(vec![" This is a misplaced inner doc comment."])
313                )],
314                [("doc-comment", Some(vec![" This is an outer doc comment."]))],
315            ]
316        );
317
318        /* struct field annotations */
319        let item = match item.value {
320            ItemKind::Struct(item) => item.fields.inner.into_iter().next().unwrap(),
321            _ => unreachable!(),
322        };
323
324        assert_eq!(
325            attributes(&item.attributes),
326            vec![
327                [(
328                    "doc-comment",
329                    Some(vec![" This is a misplaced inner doc comment."])
330                )],
331                [("doc-comment", Some(vec![" This is an outer doc comment."]))],
332                [(
333                    "doc-comment",
334                    Some(vec![" This is a misplaced inner doc comment."])
335                )],
336                [("doc-comment", Some(vec![" This is an outer doc comment."]))],
337            ]
338        );
339    }
340
341    #[test]
342    fn parse_attributes_none() {
343        let item = parse::<Item>(
344            r#"
345            fn f() -> bool {
346                false
347            }
348            "#,
349        );
350
351        assert!(matches!(item.value, ItemKind::Fn(_)));
352        assert!(item.attributes.is_empty());
353    }
354
355    #[test]
356    fn parse_attributes_fn_basic() {
357        let item = parse::<Item>(
358            r#"
359            #[foo]
360            fn f() -> bool {
361                false
362            }
363            "#,
364        );
365
366        assert!(matches!(item.value, ItemKind::Fn(_)));
367        assert_eq!(attributes(&item.attributes), vec![[("foo", None)]]);
368    }
369
370    #[test]
371    fn parse_attributes_fn_one_arg_value() {
372        let item = parse::<Item>(
373            r#"
374            #[cfg(target = "evm")]
375            fn f() -> bool {
376                false
377            }
378            "#,
379        );
380
381        assert!(matches!(item.value, ItemKind::Fn(_)));
382        assert_eq!(
383            attributes(&item.attributes),
384            vec![[("cfg", Some(vec!["target"]))]]
385        );
386    }
387
388    #[test]
389    fn parse_attributes_fn_two_arg_values() {
390        let item = parse::<Item>(
391            r#"
392            #[cfg(target = "evm", feature = "test")]
393            fn f() -> bool {
394                false
395            }
396            "#,
397        );
398
399        assert!(matches!(item.value, ItemKind::Fn(_)));
400        assert_eq!(
401            attributes(&item.attributes),
402            vec![[("cfg", Some(vec!["target", "feature"]))]]
403        );
404    }
405
406    #[test]
407    fn parse_attributes_fn_two_basic() {
408        let item = parse::<Item>(
409            r#"
410            #[foo]
411            #[bar]
412            fn f() -> bool {
413                false
414            }
415            "#,
416        );
417
418        assert!(matches!(item.value, ItemKind::Fn(_)));
419
420        assert_eq!(
421            attributes(&item.attributes),
422            vec![[("foo", None)], [("bar", None)]]
423        );
424    }
425
426    #[test]
427    fn parse_attributes_fn_one_arg() {
428        let item = parse::<Item>(
429            r#"
430            #[foo(one)]
431            fn f() -> bool {
432                false
433            }
434            "#,
435        );
436
437        assert!(matches!(item.value, ItemKind::Fn(_)));
438        assert_eq!(
439            attributes(&item.attributes),
440            vec![[("foo", Some(vec!["one"]))]]
441        );
442    }
443
444    #[test]
445    fn parse_attributes_fn_empty_parens() {
446        let item = parse::<Item>(
447            r#"
448            #[foo()]
449            fn f() -> bool {
450                false
451            }
452            "#,
453        );
454
455        assert!(matches!(item.value, ItemKind::Fn(_)));
456        assert_eq!(attributes(&item.attributes), vec![[("foo", Some(vec![]))]]);
457    }
458
459    #[test]
460    fn parse_attributes_fn_zero_and_one_arg() {
461        let item = parse::<Item>(
462            r#"
463            #[bar]
464            #[foo(one)]
465            fn f() -> bool {
466                false
467            }
468            "#,
469        );
470
471        assert!(matches!(item.value, ItemKind::Fn(_)));
472        assert_eq!(
473            attributes(&item.attributes),
474            vec![[("bar", None)], [("foo", Some(vec!["one"]))]]
475        );
476    }
477
478    #[test]
479    fn parse_attributes_fn_one_and_zero_arg() {
480        let item = parse::<Item>(
481            r#"
482            #[foo(one)]
483            #[bar]
484            fn f() -> bool {
485                false
486            }
487            "#,
488        );
489
490        assert!(matches!(item.value, ItemKind::Fn(_)));
491        assert_eq!(
492            attributes(&item.attributes),
493            vec![[("foo", Some(vec!["one"]))], [("bar", None)]]
494        );
495    }
496
497    #[test]
498    fn parse_attributes_fn_two_args() {
499        let item = parse::<Item>(
500            r#"
501            #[foo(one, two)]
502            fn f() -> bool {
503                false
504            }
505            "#,
506        );
507
508        assert!(matches!(item.value, ItemKind::Fn(_)));
509        assert_eq!(
510            attributes(&item.attributes),
511            vec![[("foo", Some(vec!["one", "two"]))]]
512        );
513    }
514
515    #[test]
516    fn parse_attributes_fn_zero_one_and_three_args() {
517        let item = parse::<Item>(
518            r#"
519            #[bar]
520            #[foo(one)]
521            #[baz(two,three,four)]
522            fn f() -> bool {
523                false
524            }
525            "#,
526        );
527
528        assert!(matches!(item.value, ItemKind::Fn(_)));
529        assert_eq!(
530            attributes(&item.attributes),
531            vec![
532                [("bar", None)],
533                [("foo", Some(vec!["one"]))],
534                [("baz", Some(vec!["two", "three", "four"]))]
535            ]
536        );
537    }
538
539    #[test]
540    fn parse_attributes_fn_zero_one_and_three_args_in_one_attribute_decl() {
541        let item = parse::<Item>(
542            r#"
543            #[bar, foo(one), baz(two,three,four)]
544            fn f() -> bool {
545                false
546            }
547            "#,
548        );
549
550        assert!(matches!(item.value, ItemKind::Fn(_)));
551        assert_eq!(
552            attributes(&item.attributes),
553            vec![[
554                ("bar", None),
555                ("foo", Some(vec!["one"])),
556                ("baz", Some(vec!["two", "three", "four"]))
557            ]]
558        );
559    }
560
561    #[test]
562    fn parse_attributes_trait() {
563        let item = parse::<Item>(
564            r#"
565            trait T {
566                #[foo(one)]
567                #[bar]
568                fn f() -> bool;
569            } {
570                #[bar(one, two, three)]
571                fn g() -> bool {
572                    f()
573                }
574            }
575            "#,
576        );
577
578        // The trait itself has no attributes.
579        assert!(matches!(item.value, ItemKind::Trait(_)));
580        assert_eq!(item.attributes.len(), 0);
581
582        if let ItemKind::Trait(item_trait) = item.value {
583            let mut decls = item_trait.trait_items.get().iter();
584
585            let trait_item = decls.next();
586            assert!(trait_item.is_some());
587            let annotated = trait_item.unwrap();
588            if let ItemTraitItem::Fn(_fn_sig, _) = &annotated.value {
589                assert_eq!(
590                    attributes(&annotated.attributes),
591                    vec![[("foo", Some(vec!["one"]))], [("bar", None)]]
592                );
593            }
594
595            assert!(decls.next().is_none());
596
597            assert!(item_trait.trait_defs_opt.is_some());
598            let mut defs = item_trait.trait_defs_opt.as_ref().unwrap().get().iter();
599
600            let g_sig = defs.next();
601            assert!(g_sig.is_some());
602
603            assert_eq!(
604                attributes(&g_sig.unwrap().attributes),
605                vec![[("bar", Some(vec!["one", "two", "three"]))],]
606            );
607
608            assert!(defs.next().is_none());
609        } else {
610            panic!("Parsed trait is not a trait.");
611        }
612    }
613
614    #[test]
615    fn parse_attributes_abi() {
616        let item = parse::<Item>(
617            r#"
618            abi A {
619                #[bar(one, two, three)]
620                fn f() -> bool;
621
622                #[foo]
623                fn g() -> u64;
624            } {
625                #[baz(one)]
626                fn h() -> bool {
627                    f()
628                }
629            }
630            "#,
631        );
632
633        // The ABI itself has no attributes.
634        assert!(matches!(item.value, ItemKind::Abi(_)));
635        assert_eq!(item.attributes.len(), 0);
636
637        if let ItemKind::Abi(item_abi) = item.value {
638            let mut decls = item_abi.abi_items.get().iter();
639
640            let f_sig = decls.next();
641            assert!(f_sig.is_some());
642
643            assert_eq!(
644                attributes(&f_sig.unwrap().attributes),
645                vec![[("bar", Some(vec!["one", "two", "three"]))],]
646            );
647
648            let g_sig = decls.next();
649            assert!(g_sig.is_some());
650
651            assert_eq!(
652                attributes(&g_sig.unwrap().attributes),
653                vec![[("foo", None)],]
654            );
655            assert!(decls.next().is_none());
656
657            assert!(item_abi.abi_defs_opt.is_some());
658            let mut defs = item_abi.abi_defs_opt.as_ref().unwrap().get().iter();
659
660            let h_sig = defs.next();
661            assert!(h_sig.is_some());
662
663            assert_eq!(
664                attributes(&h_sig.unwrap().attributes),
665                vec![[("baz", Some(vec!["one"]))],]
666            );
667            assert!(defs.next().is_none());
668        } else {
669            panic!("Parsed ABI is not an ABI.");
670        }
671    }
672
673    #[test]
674    fn parse_attributes_doc_comment() {
675        let item = parse::<Item>(
676            r#"
677            /// This is a doc comment.
678            /// This is another doc comment.
679            fn f() -> bool {
680                false
681            }
682            "#,
683        );
684
685        assert!(matches!(item.value, ItemKind::Fn(_)));
686        assert_eq!(
687            attributes(&item.attributes),
688            vec![
689                [("doc-comment", Some(vec![" This is a doc comment."]))],
690                [("doc-comment", Some(vec![" This is another doc comment."]))]
691            ]
692        );
693    }
694}