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