move_syn/
lib.rs

1#![cfg_attr(all(doc, not(doctest)), feature(doc_auto_cfg))]
2
3//! Move syntax parsing using [`unsynn`](::unsynn).
4
5use std::borrow::Cow;
6use std::collections::HashMap;
7
8pub use unsynn;
9use unsynn::*;
10
11#[cfg(test)]
12mod tests;
13
14/// Process raw Move code so that it can be used as input to Rust's tokenizer.
15///
16/// Move's and Rust's tokens are very similar, with the exception of raw identifiers for which Move
17/// uses the syntax "`ident`".
18///
19/// This function the backticks around identifiers, if found. Thus, we can re-use Rust's tokenizer
20/// afterwards, implemented by the [`proc_macro2`] crate. This is relevant because
21/// [`unsynn!`]-generated types requires Rust's [`TokenStream`] as input for parsing.
22pub fn sanitize_for_tokenizer(content: &str) -> String {
23    let regex = raw_ident_regex();
24    let mut lines = content.lines().map(|line| {
25        // Ignore commented or doc lines
26        if !line.trim_start().starts_with("//") {
27            regex.replace(line, "$1")
28        } else {
29            Cow::Borrowed(line)
30        }
31    });
32    lines.next().map_or_else(String::new, |line| {
33        let mut sanitized = String::with_capacity(content.len());
34        sanitized.push_str(&line);
35        for line in lines {
36            sanitized.push('\n');
37            sanitized.push_str(&line);
38        }
39        sanitized
40    })
41}
42
43fn raw_ident_regex() -> regex::Regex {
44    regex::Regex::new("`([[:alnum:]_]+)`").expect("Valid regex")
45}
46
47pub mod kw {
48    //! Move keywords.
49    use unsynn::*;
50
51    unsynn! {
52        pub keyword Struct = "struct";
53        pub keyword Phantom = "phantom";
54        pub keyword Public = "public";
55        pub keyword Has = "has";
56        pub keyword Copy = "copy";
57        pub keyword Drop = "drop";
58        pub keyword Key = "key";
59        pub keyword Store = "store";
60        pub keyword Module = "module";
61        pub keyword Package = "package";
62        pub keyword Friend = "friend";
63        pub keyword Use = "use";
64        pub keyword Fun = "fun";
65        pub keyword As = "as";
66        pub keyword Const = "const";
67        pub keyword Mut = "mut";
68        pub keyword Entry = "entry";
69        pub keyword Native = "native";
70        pub keyword Macro = "macro";
71        pub keyword Vector = "vector";
72        pub keyword Enum = "enum";
73    }
74}
75
76unsynn! {
77    pub enum File {
78        /// A Move file in the 2024 recommended format.
79        ModuleLabel(LabeledModule),
80        /// A Move file in the legacy style.
81        Legacy(Vec<Module>),
82    }
83
84    /// A single module defined with a top-level [label].
85    ///
86    /// [label]: https://move-book.com/guides/code-quality-checklist#using-module-label
87    pub struct LabeledModule {
88        attrs: Vec<Attribute>,
89        keyword: kw::Module,
90        named_address: Ident,
91        path_sep: PathSep,
92        ident: Ident,
93        semicolon: Semicolon,
94        contents: Vec<Item>,
95    }
96
97    /// A Move module declaration.
98    pub struct Module {
99        pub attrs: Vec<Attribute>,
100        keyword: kw::Module,
101        pub named_address: Ident,
102        path_sep: PathSep,
103        pub ident: Ident,
104        contents: BraceGroupContaining<Vec<Item>>,
105    }
106
107    /// A Move language item.
108    pub struct Item {
109        pub attrs: Vec<Attribute>,
110        vis: Option<Visibility>,
111        pub kind: ItemKind,
112    }
113
114    // === Attributes ===
115
116    /// An attribute like `#[test_only]`, `#[allow(...)]`, doc comment (`/// ...`), etc.
117    #[derive(Clone)]
118    pub struct Attribute {
119        pound: Pound,
120        contents: BracketGroupContaining<AttributeContent>,
121    }
122
123    #[derive(Clone)]
124    enum AttributeContent {
125        Doc(Cons<DocKw, Assign, LiteralString>),
126        Other(Vec<TokenTree>),
127    }
128
129    keyword DocKw = "doc";
130
131    // === Visibility modifiers ===
132
133    /// Move item visibility.
134    ///
135    /// `public`, `public(package)`, `public(friend)`
136    #[derive(Clone)]
137    struct Visibility {
138        public: kw::Public,
139        modifier: Option<ParenthesisGroupContaining<VisibilityModifier>>,
140    }
141
142    /// Move item visibility modifier.
143    ///
144    /// Examples:
145    /// - `public(package)`
146    /// - `public(friend)`
147    #[derive(Clone)]
148    enum VisibilityModifier {
149        Package(kw::Package),
150        Friend(kw::Friend)
151    }
152
153    // === ===
154
155    /// All Move item types.
156    #[non_exhaustive]
157    pub enum ItemKind {
158        Struct(Struct),
159        Enum(Enum),
160        Import(Import),
161        UseFun(UseFun),
162        Const(Const),
163        Function(Function),
164        MacroFun(MacroFun),
165        NativeFun(NativeFun)
166    }
167
168    pub struct UseFun {
169        keyword: kw::Use,
170        fun_kw: kw::Fun,
171        path_prefix: Option<Cons<Ident, PathSep, Ident, PathSep>>,
172        fun: Ident,
173        as_kw: kw::As,
174        ty: Ident,
175        dot: Dot,
176        method: Ident,
177        semicolon: Semicolon,
178    }
179
180    // === Constants ===
181
182    pub struct Const {
183        const_kw: kw::Const,
184        ident: Ident,
185        colon: Colon,
186        ty: Type,
187        assign: Assign,
188        expr: ConstVal,
189        semicolon: Semicolon,
190    }
191
192    enum ConstVal {
193        Literal(Literal),
194        Vector(Cons<kw::Vector, BracketGroup>),
195        NamedAddress(Cons<At, Literal>),
196        // Hack to parse anything until (but excluding) a `;`
197        Expr(Vec<Cons<Except<Semicolon>, TokenTree>>),
198    }
199
200    // === Imports ===
201
202    pub struct Import {
203        keyword: kw::Use,
204        named_address: Ident,
205        path_sep: PathSep,
206        module: ImportModule,
207        semicolon: Semicolon,
208    }
209
210    /// `module`, `module as alias`, `module::...`, `{module, ...}`
211    enum ImportModule {
212        One(ModuleOrItems),
213        Many(BraceGroupContaining<CommaDelimitedVec<ModuleOrItems>>),
214    }
215
216    #[derive(Clone)]
217    struct ModuleOrItems {
218        ident: Ident,
219        next: Option<AliasOrItems>,
220    }
221
222    #[derive(Clone)]
223    enum AliasOrItems {
224        Alias {
225            as_kw: kw::As,
226            alias: Ident,
227        },
228        Items {
229            sep: PathSep,
230            item: ImportItem,
231        }
232    }
233
234    #[derive(Clone)]
235    enum ImportItem {
236        One(MaybeAliased),
237        Many(BraceGroupContaining<CommaDelimitedVec<MaybeAliased>>)
238    }
239
240    #[derive(Clone)]
241    struct MaybeAliased {
242        ident: Ident,
243        alias: Option<Cons<kw::As, Ident>>,
244    }
245
246    // === Structs ===
247
248    /// A Move struct.
249    #[derive(Clone)]
250    pub struct Struct {
251        keyword: kw::Struct,
252        pub ident: Ident,
253        pub generics: Option<Generics>,
254        pub kind: StructKind,
255    }
256
257    /// The kinds of structs; either a braced or tuple one.
258    #[derive(Clone)]
259    pub enum StructKind {
260        Braced(BracedStruct),
261        Tuple(TupleStruct),
262    }
263
264    /// Braced structs have their abilities declared before their fields.
265    #[derive(Clone)]
266    pub struct BracedStruct {
267        abilities: Option<Abilities>,
268        pub fields: NamedFields,
269    }
270
271    /// Tuple structs have their abilities declared after their fields, with a trailing semicolon
272    /// if so.
273    #[derive(Clone)]
274    pub struct TupleStruct {
275        pub fields: PositionalFields,
276        abilities: Option<Cons<Abilities, Semicolon>>
277    }
278
279    // === Enums ===
280
281    #[derive(Clone)]
282    pub struct Enum {
283        keyword: kw::Enum,
284        pub ident: Ident,
285        pub generics: Option<Generics>,
286        pub abilities: Option<Abilities>,
287        content: BraceGroupContaining<CommaDelimitedVec<EnumVariant>>,
288    }
289
290    #[derive(Clone)]
291    pub struct EnumVariant {
292        pub attrs: Vec<Attribute>,
293        pub ident: Ident,
294        /// The fields of the enum variants. If none, it's a "unit" or "empty" variant.
295        pub fields: Option<FieldsKind>
296    }
297
298    /// Kinds of fields for a Move enum.
299    #[derive(Clone)]
300    pub enum FieldsKind {
301        Positional(PositionalFields),
302        Named(NamedFields),
303    }
304
305    // === Datatype fields ===
306
307    /// Parenthesis group containing comma-delimited unnamed fields.
308    #[derive(Clone)]
309    pub struct PositionalFields(ParenthesisGroupContaining<DelimitedVec<UnnamedField, Comma>>);
310
311    /// Brace group containing comma-delimited named fields.
312    #[derive(Clone)]
313    pub struct NamedFields(BraceGroupContaining<DelimitedVec<NamedField, Comma>>);
314
315    /// Named datatype field.
316    #[derive(Clone)]
317    pub struct NamedField {
318        pub attrs: Vec<Attribute>,
319        pub ident: Ident,
320        colon: Colon,
321        pub ty: Type,
322    }
323
324    /// Unnamed datatype field.
325    #[derive(Clone)]
326    pub struct UnnamedField {
327        pub attrs: Vec<Attribute>,
328        pub ty: Type,
329    }
330
331    // === Generics ===
332
333    /// The generics of a datatype or function.
334    ///
335    /// # Example
336    /// `<T, U: drop, V: key + store>`
337    #[derive(Clone)]
338    pub struct Generics {
339        lt_token: Lt,
340        type_args: DelimitedVec<Generic, Comma>,
341        gt_token: Gt,
342    }
343
344    /// A generic type declaration.
345    ///
346    /// # Examples
347    /// * `T`
348    /// * `T: drop`
349    /// * `T: key + store`
350    /// * `phantom T`
351    #[derive(Clone)]
352    pub struct Generic {
353        pub phantom: Option<kw::Phantom>,
354        pub ident: Ident,
355        bounds: Option<GenericBounds>
356    }
357
358    /// Slightly convoluted, but captures the fact that:
359    /// * `:` must be followed by an ability
360    /// * additional abilities are preceeded by `+`
361    #[derive(Clone)]
362    struct GenericBounds {
363        colon: Colon,
364        first_ability: Ability,
365        extra_abilities: Vec<Cons<Plus, Ability>>
366    }
367
368    // === Abilities ===
369
370    /// Abilities declaration for a datatype.
371    ///
372    /// Example: `has key, store`
373    #[derive(Clone)]
374    struct Abilities {
375        has: kw::Has,
376        keywords: Many<Ability, Comma>,
377    }
378
379    /// Ability keywords.
380    #[derive(Clone)]
381    pub enum Ability {
382        Copy(kw::Copy),
383        Drop(kw::Drop),
384        Key(kw::Key),
385        Store(kw::Store),
386    }
387
388    // === Types ===
389
390    /// Type of function arguments or returns.
391    struct MaybeRefType {
392        r#ref: Option<Ref>,
393        r#type: Type,
394    }
395
396    /// The reference prefix
397    struct Ref {
398        and: And,
399        r#mut: Option<kw::Mut>,
400    }
401
402    /// Non-reference type, used in datatype fields.
403    #[derive(Clone)]
404    pub struct Type {
405        pub path: TypePath,
406        pub type_args: Option<TypeArgs>
407    }
408
409    /// Path to a type.
410    #[derive(Clone)]
411    pub enum TypePath {
412        /// Fully qualified,
413        Full {
414            named_address: Ident,
415            sep0: PathSep,
416            module: Ident,
417            sep1: PathSep,
418            r#type: Ident,
419        },
420        /// Module prefix only, if it was imported already.
421        Module {
422            module: Ident,
423            sep: PathSep,
424            r#type: Ident,
425        },
426        /// Only the type identifier.
427        Ident(Ident),
428    }
429
430    /// Angle bracket group (`<...>`) containing comma-delimited types.
431    #[derive(Clone)]
432    pub struct TypeArgs {
433        lt: Lt,
434        args: Many<Box<Type>, Comma>,
435        gt: Gt,
436    }
437
438    // === Functions ===
439
440    pub struct NativeFun {
441        native_kw: kw::Native,
442        fun_kw: kw::Fun,
443        ident: Ident,
444        generics: Option<Generics>,
445        args: ParenthesisGroup,
446        ret: Option<Cons<Colon, Either<MaybeRefType, ParenthesisGroup>>>,
447        semicolon: Semicolon
448    }
449
450    pub struct Function {
451        entry: Option<kw::Entry>,
452        fun_kw: kw::Fun,
453        ident: Ident,
454        generics: Option<Generics>,
455        args: ParenthesisGroup,
456        ret: Option<Cons<Colon, Either<MaybeRefType, ParenthesisGroup>>>,
457        body: BraceGroup,
458    }
459
460    // === Macros ===
461
462    pub struct MacroFun {
463        macro_kw: kw::Macro,
464        fun_kw: kw::Fun,
465        ident: Ident,
466        generics: Option<MacroGenerics>,
467        args: ParenthesisGroup,
468        ret: Option<Cons<Colon, Either<MacroReturn, ParenthesisGroup>>>,
469        body: BraceGroup,
470    }
471
472    struct MacroGenerics {
473        lt_token: Lt,
474        type_args: DelimitedVec<MacroTypeArg, Comma>,
475        gt_token: Gt,
476    }
477
478    /// `$T: drop + store`
479    struct MacroTypeArg{
480        name: MacroTypeName,
481        bounds: Option<GenericBounds>,
482    }
483
484    /// Either `_` or a 'concrete' type
485    enum MacroReturn {
486        Underscore(Underscore),
487        Concrete(Cons<Option<Ref>, MacroReturnType>),
488    }
489
490    /// Return type for macro funs.
491    ///
492    /// - `$T`
493    /// - `&mut $T`
494    /// - `&String`
495    /// - `Option<$T>`
496    enum MacroReturnType {
497        MacroTypeName(MacroTypeName),
498        Hybrid(HybridMacroType)
499    }
500
501    struct HybridMacroType {
502        ident: Ident,
503        type_args: Option<Cons<Lt, Many<Either<Type, MacroTypeName, Box<HybridMacroType>>, Comma>, Gt>>
504    }
505
506    /// `$T`
507    struct MacroTypeName {
508        dollar: Dollar,
509        ident: Ident,
510    }
511}
512
513impl File {
514    pub fn into_modules(self) -> impl Iterator<Item = Module> {
515        match self {
516            Self::ModuleLabel(labeled) => std::iter::once(labeled.into_module()).boxed(),
517            Self::Legacy(modules) => modules.into_iter().boxed(),
518        }
519    }
520}
521
522impl LabeledModule {
523    pub fn into_module(self) -> Module {
524        Module {
525            attrs: self.attrs,
526            keyword: self.keyword,
527            named_address: self.named_address,
528            path_sep: self.path_sep,
529            ident: self.ident,
530            contents: BraceGroupContaining {
531                content: self.contents,
532            },
533        }
534    }
535}
536
537impl Module {
538    /// Add `sui` implicit imports as explicit `use` statements to the module.
539    ///
540    /// [Reference](https://move-book.com/programmability/sui-framework#implicit-imports)
541    pub fn with_implicit_sui_imports(&mut self) -> &mut Self {
542        // Build the map of implicit imports keyed by the identifiers they export.
543        let implicit_imports: HashMap<_, _> = [
544            "use sui::object;",
545            "use sui::object::ID;",
546            "use sui::object::UID;",
547            "use sui::tx_context;",
548            "use sui::tx_context::TxContext;",
549            "use sui::transfer;",
550        ]
551        .into_iter()
552        .map(|text| {
553            text.to_token_iter()
554                .parse_all::<Import>()
555                .expect("Valid imports")
556        })
557        .map(|import| {
558            let ident = import
559                .imported_idents()
560                .next()
561                .expect("Each import exposes exactly one ident");
562            (ident.clone(), import)
563        })
564        .collect();
565
566        self.add_implicit_imports(implicit_imports)
567    }
568
569    /// Add `iota` implicit imports as explicit `use` statements to the module.
570    ///
571    /// Adapted from the `sui` equivalents.
572    pub fn with_implicit_iota_imports(&mut self) -> &mut Self {
573        // Build the map of implicit imports keyed by the identifiers they export.
574        let implicit_imports: HashMap<_, _> = [
575            "use iota::object;",
576            "use iota::object::ID;",
577            "use iota::object::UID;",
578            "use iota::tx_context;",
579            "use iota::tx_context::TxContext;",
580            "use iota::transfer;",
581        ]
582        .into_iter()
583        .map(|text| {
584            text.to_token_iter()
585                .parse_all::<Import>()
586                .expect("Valid imports")
587        })
588        .map(|import| {
589            let ident = import
590                .imported_idents()
591                .next()
592                .expect("Each import exposes exactly one ident");
593            (ident.clone(), import)
594        })
595        .collect();
596
597        self.add_implicit_imports(implicit_imports)
598    }
599
600    /// Resolve all datatype field types to their fully-qualified paths.
601    pub fn fully_qualify_datatype_field_types(&mut self) -> &mut Self {
602        let imports: HashMap<_, _> = self
603            .items()
604            .filter_map(|item| match &item.kind {
605                ItemKind::Import(import) => Some(import),
606                _ => None,
607            })
608            .flat_map(|import| import.flatten())
609            .collect();
610
611        for datatype in self.datatypes_mut() {
612            let generics = datatype.generics();
613            for type_ in datatype.field_types_mut() {
614                type_.resolve(&imports, &generics);
615            }
616        }
617        self
618    }
619
620    pub fn items(&self) -> impl Iterator<Item = &Item> {
621        self.contents.content.iter()
622    }
623
624    #[cfg(test)]
625    pub fn into_items(self) -> impl Iterator<Item = Item> {
626        self.contents.content.into_iter()
627    }
628
629    fn datatypes_mut(&mut self) -> impl Iterator<Item = &mut dyn Datatype> {
630        self.contents.content.iter_mut().filter_map(|item| {
631            Some(match &mut item.kind {
632                ItemKind::Enum(e) => e as _,
633                ItemKind::Struct(s) => s as _,
634                _ => return None,
635            })
636        })
637    }
638
639    fn add_implicit_imports(&mut self, mut implicit_imports: HashMap<Ident, Import>) -> &mut Self {
640        // Filter out any that were shadowed by existing imports
641        for item in self.items() {
642            let ItemKind::Import(import) = &item.kind else {
643                continue;
644            };
645            for ident in import.imported_idents() {
646                implicit_imports.remove(ident);
647            }
648        }
649
650        // Add the remaining implicit imports to the list of module items
651        for (_, import) in implicit_imports {
652            self.contents.content.push(Item {
653                attrs: vec![],
654                vis: None,
655                kind: ItemKind::Import(import),
656            })
657        }
658        self
659    }
660}
661
662impl Import {
663    /// List of idents (or aliases) brought into scope by this import and their paths
664    /// (`named_address::module(::item)?`).
665    fn flatten(&self) -> impl Iterator<Item = (Ident, FlatImport)> + '_ {
666        let named_address = self.named_address.clone();
667        match &self.module {
668            // use named_address::module...
669            ImportModule::One(module_or_items) => module_or_items.flatten(named_address),
670            // use named_address::{...}
671            ImportModule::Many(BraceGroupContaining {
672                content: DelimitedVec(ms),
673            }) => ms
674                .iter()
675                .flat_map(move |Delimited { value, .. }| value.flatten(named_address.clone()))
676                .boxed(),
677        }
678    }
679
680    /// The list of item idents brought into scope by this import.
681    fn imported_idents(&self) -> impl Iterator<Item = &Ident> {
682        match &self.module {
683            ImportModule::One(module_or_items) => module_or_items.available_idents(),
684            ImportModule::Many(BraceGroupContaining {
685                content: DelimitedVec(ms),
686            }) => ms
687                .iter()
688                .flat_map(|delimited| delimited.value.available_idents())
689                .boxed(),
690        }
691    }
692}
693
694impl ModuleOrItems {
695    /// Flat canonical imports (`named_address::module(::item)?`).
696    fn flatten(&self, named_address: Ident) -> Box<dyn Iterator<Item = (Ident, FlatImport)> + '_> {
697        let module = self.ident.clone();
698
699        let Some(next) = &self.next else {
700            // module;
701            return std::iter::once((
702                module.clone(),
703                FlatImport::Module {
704                    named_address,
705                    module,
706                },
707            ))
708            .boxed();
709        };
710
711        match next {
712            // module as alias;
713            AliasOrItems::Alias { alias, .. } => std::iter::once((
714                alias.clone(),
715                FlatImport::Module {
716                    named_address,
717                    module,
718                },
719            ))
720            .boxed(),
721
722            // module::item( as alias)?;
723            AliasOrItems::Items {
724                item: ImportItem::One(maybe_aliased),
725                ..
726            } => std::iter::once(maybe_aliased.flat_import(named_address, module)).boxed(),
727
728            // module::{(item( as alias)?),+};
729            AliasOrItems::Items {
730                item:
731                    ImportItem::Many(BraceGroupContaining {
732                        content: DelimitedVec(items),
733                    }),
734                ..
735            } => items
736                .iter()
737                .map(move |Delimited { value, .. }| {
738                    value.flat_import(named_address.clone(), module.clone())
739                })
740                .boxed(),
741        }
742    }
743
744    /// Identifiers this import makes available in scope.
745    fn available_idents(&self) -> Box<dyn Iterator<Item = &Ident> + '_> {
746        let Some(next) = &self.next else {
747            return std::iter::once(&self.ident).boxed();
748        };
749
750        match next {
751            AliasOrItems::Alias { alias, .. } => std::iter::once(alias).boxed(),
752
753            AliasOrItems::Items {
754                item: ImportItem::One(item),
755                ..
756            } => std::iter::once(item.available_ident(&self.ident)).boxed(),
757
758            AliasOrItems::Items {
759                item:
760                    ImportItem::Many(BraceGroupContaining {
761                        content: DelimitedVec(items),
762                    }),
763                ..
764            } => items
765                .iter()
766                .map(|delimited| delimited.value.available_ident(&self.ident))
767                .boxed(),
768        }
769    }
770}
771
772impl MaybeAliased {
773    /// Special handling for `Self` imports.
774    fn flat_import(&self, named_address: Ident, module: Ident) -> (Ident, FlatImport) {
775        if self.ident == "Self" {
776            (
777                self.alias().unwrap_or(&module).clone(),
778                FlatImport::Module {
779                    named_address,
780                    module,
781                },
782            )
783        } else {
784            (
785                self.alias().unwrap_or(&self.ident).clone(),
786                FlatImport::Item {
787                    named_address,
788                    module,
789                    r#type: self.ident.clone(),
790                },
791            )
792        }
793    }
794
795    fn available_ident<'a>(&'a self, module: &'a Ident) -> &'a Ident {
796        if self.ident == "Self" {
797            self.alias().unwrap_or(module)
798        } else {
799            self.alias().unwrap_or(&self.ident)
800        }
801    }
802
803    /// The identifier alias that's available in scope, if any.
804    fn alias(&self) -> Option<&Ident> {
805        self.alias.as_ref().map(|cons| &cons.second)
806    }
807}
808
809impl Attribute {
810    /// Whether this is a `#[doc = "..."]`.
811    pub const fn is_doc(&self) -> bool {
812        matches!(self.contents.content, AttributeContent::Doc(_))
813    }
814
815    /// Everything inside the bracket group, `#[...]`.
816    pub const fn contents(&self) -> &impl ToTokens {
817        &self.contents.content
818    }
819}
820
821impl ItemKind {
822    /// Whether this item is a datatype (enum/struct) declaration.
823    pub const fn is_datatype(&self) -> bool {
824        matches!(self, Self::Enum(_) | Self::Struct(_))
825    }
826}
827
828impl Struct {
829    pub fn abilities(&self) -> impl Iterator<Item = &Ability> {
830        use StructKind as K;
831        match &self.kind {
832            K::Braced(braced) => braced
833                .abilities
834                .iter()
835                .flat_map(|a| a.keywords.0.iter())
836                .map(|d| &d.value)
837                .boxed(),
838            K::Tuple(tuple) => tuple
839                .abilities
840                .iter()
841                .flat_map(|a| a.first.keywords.0.iter())
842                .map(|d| &d.value)
843                .boxed(),
844        }
845    }
846
847    pub fn field_types_mut(&mut self) -> impl Iterator<Item = &mut Type> {
848        use StructKind as K;
849        match &mut self.kind {
850            K::Tuple(TupleStruct {
851                fields: contents, ..
852            }) => contents.types_mut().boxed(),
853            K::Braced(BracedStruct {
854                fields: contents, ..
855            }) => contents.types_mut().boxed(),
856        }
857    }
858
859    fn fields_group_mut(&mut self) -> &mut dyn FieldsGroup {
860        match &mut self.kind {
861            StructKind::Braced(braced) => &mut braced.fields,
862            StructKind::Tuple(tuple) => &mut tuple.fields,
863        }
864    }
865}
866
867impl BracedStruct {
868    pub fn fields(&self) -> impl Iterator<Item = &NamedField> + Clone + '_ {
869        self.fields.fields()
870    }
871
872    /// Whether this struct has no fields.
873    pub const fn is_empty(&self) -> bool {
874        self.fields.is_empty()
875    }
876}
877
878impl TupleStruct {
879    pub fn fields(&self) -> impl Iterator<Item = &UnnamedField> + Clone + '_ {
880        self.fields.fields()
881    }
882
883    /// Whether this struct has no fields.
884    pub const fn is_empty(&self) -> bool {
885        self.fields.is_empty()
886    }
887}
888
889impl Enum {
890    pub fn abilities(&self) -> impl Iterator<Item = &Ability> {
891        self.abilities
892            .iter()
893            .flat_map(|a| a.keywords.0.iter())
894            .map(|d| &d.value)
895    }
896
897    pub fn variants(&self) -> impl Iterator<Item = &EnumVariant> {
898        self.content
899            .content
900            .0
901            .iter()
902            .map(|Delimited { value, .. }| value)
903    }
904
905    fn field_groups_mut(&mut self) -> impl Iterator<Item = &mut dyn FieldsGroup> {
906        self.content
907            .content
908            .0
909            .iter_mut()
910            .flat_map(|Delimited { value, .. }| value.fields_mut())
911    }
912}
913
914impl EnumVariant {
915    fn fields_mut(&mut self) -> Option<&mut dyn FieldsGroup> {
916        self.fields.as_mut().map(|x| x as _)
917    }
918}
919
920impl NamedFields {
921    pub fn fields(&self) -> impl Iterator<Item = &NamedField> + Clone + '_ {
922        self.0.content.0.iter().map(|d| &d.value)
923    }
924
925    pub const fn is_empty(&self) -> bool {
926        self.0.content.0.is_empty()
927    }
928}
929
930impl PositionalFields {
931    pub const fn new() -> Self {
932        Self(ParenthesisGroupContaining {
933            content: DelimitedVec(vec![]),
934        })
935    }
936
937    pub fn fields(&self) -> impl Iterator<Item = &UnnamedField> + Clone + '_ {
938        self.0.content.0.iter().map(|d| &d.value)
939    }
940
941    pub const fn is_empty(&self) -> bool {
942        self.0.content.0.is_empty()
943    }
944}
945
946impl Default for PositionalFields {
947    fn default() -> Self {
948        Self::new()
949    }
950}
951
952impl Type {
953    /// Resolve the types' path to a fully-qualified declaration, recursively.
954    fn resolve(&mut self, imports: &HashMap<Ident, FlatImport>, generics: &[Ident]) {
955        use TypePath as P;
956        // First, resolve the type arguments
957        for ty in self.type_args_mut() {
958            ty.resolve(imports, generics);
959        }
960
961        // Then resolve its own path
962        // HACK: We trust the Move code is valid, so the expected import should always be found,
963        // hence we don't error/panic if it isn't
964        let resolved = match &self.path {
965            P::Module { module, r#type, .. } => {
966                let Some(FlatImport::Module {
967                    named_address,
968                    module,
969                }) = imports.get(module)
970                else {
971                    return;
972                };
973                P::Full {
974                    named_address: named_address.clone(),
975                    sep0: PathSep::default(),
976                    module: module.clone(),
977                    sep1: PathSep::default(),
978                    r#type: r#type.clone(),
979                }
980            }
981            P::Ident(ident) if !generics.contains(ident) => {
982                let Some(FlatImport::Item {
983                    named_address,
984                    module,
985                    r#type,
986                }) = imports.get(ident)
987                else {
988                    return;
989                };
990                P::Full {
991                    named_address: named_address.clone(),
992                    sep0: PathSep::default(),
993                    module: module.clone(),
994                    sep1: PathSep::default(),
995                    r#type: r#type.clone(),
996                }
997            }
998            // Already fully-qualified types or idents shadowed by generics should be left alone
999            _ => return,
1000        };
1001        self.path = resolved;
1002    }
1003
1004    fn type_args_mut(&mut self) -> impl Iterator<Item = &mut Self> {
1005        self.type_args
1006            .iter_mut()
1007            .flat_map(|args| args.args.0.iter_mut().map(|d| &mut *d.value))
1008    }
1009}
1010
1011impl TypeArgs {
1012    /// Guaranteed to be non-empty.
1013    pub fn types(&self) -> impl Iterator<Item = &Type> {
1014        self.args.0.iter().map(|args| &*args.value)
1015    }
1016}
1017
1018impl Generics {
1019    pub fn generics(&self) -> impl Iterator<Item = &Generic> + '_ {
1020        self.type_args.0.iter().map(|d| &d.value)
1021    }
1022}
1023
1024// === Non-lang items ===
1025
1026#[cfg_attr(test, derive(derive_more::Display))]
1027enum FlatImport {
1028    #[cfg_attr(test, display("{named_address}::{module}"))]
1029    Module { named_address: Ident, module: Ident },
1030    #[cfg_attr(test, display("{named_address}::{module}::{type}"))]
1031    Item {
1032        named_address: Ident,
1033        module: Ident,
1034        r#type: Ident,
1035    },
1036}
1037
1038// === Misc helpers ===
1039
1040/// Box an iterator, necessary when returning different types that implement [`Iterator`].
1041trait IteratorBoxed<'a>: Iterator + 'a {
1042    fn boxed(self) -> Box<dyn Iterator<Item = Self::Item> + 'a>
1043    where
1044        Self: Sized,
1045    {
1046        Box::new(self)
1047    }
1048}
1049
1050impl<'a, T> IteratorBoxed<'a> for T where T: Iterator + 'a {}
1051
1052/// An enum or struct.
1053trait Datatype {
1054    fn generics(&self) -> Vec<Ident>;
1055
1056    fn field_types_mut(&mut self) -> Box<dyn Iterator<Item = &mut Type> + '_>;
1057}
1058
1059impl Datatype for Enum {
1060    fn generics(&self) -> Vec<Ident> {
1061        self.generics
1062            .iter()
1063            .flat_map(|generics| generics.generics())
1064            .map(|generic| generic.ident.clone())
1065            .collect()
1066    }
1067
1068    fn field_types_mut(&mut self) -> Box<dyn Iterator<Item = &mut Type> + '_> {
1069        self.field_groups_mut()
1070            .flat_map(|group| group.types_mut())
1071            .boxed()
1072    }
1073}
1074
1075impl Datatype for Struct {
1076    fn generics(&self) -> Vec<Ident> {
1077        self.generics
1078            .iter()
1079            .flat_map(|generics| generics.generics())
1080            .map(|generic| generic.ident.clone())
1081            .collect()
1082    }
1083
1084    fn field_types_mut(&mut self) -> Box<dyn Iterator<Item = &mut Type> + '_> {
1085        self.fields_group_mut().types_mut()
1086    }
1087}
1088
1089/// A group of named or positional datatype fields.
1090trait FieldsGroup {
1091    /// Field types. Used to resolve into fully-qualified paths.
1092    fn types_mut(&mut self) -> Box<dyn Iterator<Item = &mut Type> + '_>;
1093}
1094
1095impl FieldsGroup for FieldsKind {
1096    fn types_mut(&mut self) -> Box<dyn Iterator<Item = &mut Type> + '_> {
1097        match self {
1098            Self::Named(named) => named.types_mut(),
1099            Self::Positional(positional) => positional.types_mut(),
1100        }
1101    }
1102}
1103
1104impl FieldsGroup for NamedFields {
1105    fn types_mut(&mut self) -> Box<dyn Iterator<Item = &mut Type> + '_> {
1106        self.0
1107            .content
1108            .0
1109            .iter_mut()
1110            .map(|Delimited { value: field, .. }| &mut field.ty)
1111            .boxed()
1112    }
1113}
1114
1115impl FieldsGroup for PositionalFields {
1116    fn types_mut(&mut self) -> Box<dyn Iterator<Item = &mut Type> + '_> {
1117        self.0
1118            .content
1119            .0
1120            .iter_mut()
1121            .map(|Delimited { value: field, .. }| &mut field.ty)
1122            .boxed()
1123    }
1124}