1fad3a1d3Sopenharmony_ciuse crate::{version, workspace_path};
2fad3a1d3Sopenharmony_ciuse anyhow::{bail, Result};
3fad3a1d3Sopenharmony_ciuse indexmap::IndexMap;
4fad3a1d3Sopenharmony_ciuse quote::quote;
5fad3a1d3Sopenharmony_ciuse std::collections::BTreeMap;
6fad3a1d3Sopenharmony_ciuse std::fs;
7fad3a1d3Sopenharmony_ciuse std::path::{Path, PathBuf};
8fad3a1d3Sopenharmony_ciuse syn::parse::{Error, Parser};
9fad3a1d3Sopenharmony_ciuse syn::{
10fad3a1d3Sopenharmony_ci    parse_quote, Attribute, Data, DataEnum, DataStruct, DeriveInput, Fields, GenericArgument,
11fad3a1d3Sopenharmony_ci    Ident, Item, PathArguments, TypeMacro, TypePath, TypeTuple, UseTree, Visibility,
12fad3a1d3Sopenharmony_ci};
13fad3a1d3Sopenharmony_ciuse syn_codegen as types;
14fad3a1d3Sopenharmony_ciuse thiserror::Error;
15fad3a1d3Sopenharmony_ci
16fad3a1d3Sopenharmony_ciconst SYN_CRATE_ROOT: &str = "src/lib.rs";
17fad3a1d3Sopenharmony_ciconst TOKEN_SRC: &str = "src/token.rs";
18fad3a1d3Sopenharmony_ciconst IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
19fad3a1d3Sopenharmony_ciconst EXTRA_TYPES: &[&str] = &["Lifetime"];
20fad3a1d3Sopenharmony_ci
21fad3a1d3Sopenharmony_cistruct Lookup {
22fad3a1d3Sopenharmony_ci    items: BTreeMap<Ident, AstItem>,
23fad3a1d3Sopenharmony_ci    // "+" => "Add"
24fad3a1d3Sopenharmony_ci    tokens: BTreeMap<String, String>,
25fad3a1d3Sopenharmony_ci    // "PatLit" => "ExprLit"
26fad3a1d3Sopenharmony_ci    aliases: BTreeMap<Ident, Ident>,
27fad3a1d3Sopenharmony_ci}
28fad3a1d3Sopenharmony_ci
29fad3a1d3Sopenharmony_ci/// Parse the contents of `src` and return a list of AST types.
30fad3a1d3Sopenharmony_cipub fn parse() -> Result<types::Definitions> {
31fad3a1d3Sopenharmony_ci    let tokens = load_token_file(TOKEN_SRC)?;
32fad3a1d3Sopenharmony_ci
33fad3a1d3Sopenharmony_ci    let mut lookup = Lookup {
34fad3a1d3Sopenharmony_ci        items: BTreeMap::new(),
35fad3a1d3Sopenharmony_ci        tokens,
36fad3a1d3Sopenharmony_ci        aliases: BTreeMap::new(),
37fad3a1d3Sopenharmony_ci    };
38fad3a1d3Sopenharmony_ci
39fad3a1d3Sopenharmony_ci    load_file(SYN_CRATE_ROOT, &[], &mut lookup)?;
40fad3a1d3Sopenharmony_ci
41fad3a1d3Sopenharmony_ci    let version = version::get()?;
42fad3a1d3Sopenharmony_ci
43fad3a1d3Sopenharmony_ci    let types = lookup
44fad3a1d3Sopenharmony_ci        .items
45fad3a1d3Sopenharmony_ci        .values()
46fad3a1d3Sopenharmony_ci        .map(|item| introspect_item(item, &lookup))
47fad3a1d3Sopenharmony_ci        .collect();
48fad3a1d3Sopenharmony_ci
49fad3a1d3Sopenharmony_ci    let tokens = lookup
50fad3a1d3Sopenharmony_ci        .tokens
51fad3a1d3Sopenharmony_ci        .into_iter()
52fad3a1d3Sopenharmony_ci        .map(|(name, ty)| (ty, name))
53fad3a1d3Sopenharmony_ci        .collect();
54fad3a1d3Sopenharmony_ci
55fad3a1d3Sopenharmony_ci    Ok(types::Definitions {
56fad3a1d3Sopenharmony_ci        version,
57fad3a1d3Sopenharmony_ci        types,
58fad3a1d3Sopenharmony_ci        tokens,
59fad3a1d3Sopenharmony_ci    })
60fad3a1d3Sopenharmony_ci}
61fad3a1d3Sopenharmony_ci
62fad3a1d3Sopenharmony_ci/// Data extracted from syn source
63fad3a1d3Sopenharmony_cipub struct AstItem {
64fad3a1d3Sopenharmony_ci    ast: DeriveInput,
65fad3a1d3Sopenharmony_ci    features: Vec<Attribute>,
66fad3a1d3Sopenharmony_ci}
67fad3a1d3Sopenharmony_ci
68fad3a1d3Sopenharmony_cifn introspect_item(item: &AstItem, lookup: &Lookup) -> types::Node {
69fad3a1d3Sopenharmony_ci    let features = introspect_features(&item.features);
70fad3a1d3Sopenharmony_ci
71fad3a1d3Sopenharmony_ci    match &item.ast.data {
72fad3a1d3Sopenharmony_ci        Data::Enum(data) => types::Node {
73fad3a1d3Sopenharmony_ci            ident: item.ast.ident.to_string(),
74fad3a1d3Sopenharmony_ci            features,
75fad3a1d3Sopenharmony_ci            data: types::Data::Enum(introspect_enum(data, lookup)),
76fad3a1d3Sopenharmony_ci            exhaustive: !(is_non_exhaustive(&item.ast.attrs)
77fad3a1d3Sopenharmony_ci                || data.variants.iter().any(|v| is_doc_hidden(&v.attrs))),
78fad3a1d3Sopenharmony_ci        },
79fad3a1d3Sopenharmony_ci        Data::Struct(data) => types::Node {
80fad3a1d3Sopenharmony_ci            ident: item.ast.ident.to_string(),
81fad3a1d3Sopenharmony_ci            features,
82fad3a1d3Sopenharmony_ci            data: {
83fad3a1d3Sopenharmony_ci                if data.fields.iter().all(|f| is_pub(&f.vis)) {
84fad3a1d3Sopenharmony_ci                    types::Data::Struct(introspect_struct(data, lookup))
85fad3a1d3Sopenharmony_ci                } else {
86fad3a1d3Sopenharmony_ci                    types::Data::Private
87fad3a1d3Sopenharmony_ci                }
88fad3a1d3Sopenharmony_ci            },
89fad3a1d3Sopenharmony_ci            exhaustive: true,
90fad3a1d3Sopenharmony_ci        },
91fad3a1d3Sopenharmony_ci        Data::Union(..) => panic!("Union not supported"),
92fad3a1d3Sopenharmony_ci    }
93fad3a1d3Sopenharmony_ci}
94fad3a1d3Sopenharmony_ci
95fad3a1d3Sopenharmony_cifn introspect_enum(item: &DataEnum, lookup: &Lookup) -> types::Variants {
96fad3a1d3Sopenharmony_ci    item.variants
97fad3a1d3Sopenharmony_ci        .iter()
98fad3a1d3Sopenharmony_ci        .filter_map(|variant| {
99fad3a1d3Sopenharmony_ci            if is_doc_hidden(&variant.attrs) {
100fad3a1d3Sopenharmony_ci                return None;
101fad3a1d3Sopenharmony_ci            }
102fad3a1d3Sopenharmony_ci            let fields = match &variant.fields {
103fad3a1d3Sopenharmony_ci                Fields::Unnamed(fields) => fields
104fad3a1d3Sopenharmony_ci                    .unnamed
105fad3a1d3Sopenharmony_ci                    .iter()
106fad3a1d3Sopenharmony_ci                    .map(|field| introspect_type(&field.ty, lookup))
107fad3a1d3Sopenharmony_ci                    .collect(),
108fad3a1d3Sopenharmony_ci                Fields::Unit => vec![],
109fad3a1d3Sopenharmony_ci                Fields::Named(_) => panic!("Enum representation not supported"),
110fad3a1d3Sopenharmony_ci            };
111fad3a1d3Sopenharmony_ci            Some((variant.ident.to_string(), fields))
112fad3a1d3Sopenharmony_ci        })
113fad3a1d3Sopenharmony_ci        .collect()
114fad3a1d3Sopenharmony_ci}
115fad3a1d3Sopenharmony_ci
116fad3a1d3Sopenharmony_cifn introspect_struct(item: &DataStruct, lookup: &Lookup) -> types::Fields {
117fad3a1d3Sopenharmony_ci    match &item.fields {
118fad3a1d3Sopenharmony_ci        Fields::Named(fields) => fields
119fad3a1d3Sopenharmony_ci            .named
120fad3a1d3Sopenharmony_ci            .iter()
121fad3a1d3Sopenharmony_ci            .map(|field| {
122fad3a1d3Sopenharmony_ci                (
123fad3a1d3Sopenharmony_ci                    field.ident.as_ref().unwrap().to_string(),
124fad3a1d3Sopenharmony_ci                    introspect_type(&field.ty, lookup),
125fad3a1d3Sopenharmony_ci                )
126fad3a1d3Sopenharmony_ci            })
127fad3a1d3Sopenharmony_ci            .collect(),
128fad3a1d3Sopenharmony_ci        Fields::Unit => IndexMap::new(),
129fad3a1d3Sopenharmony_ci        Fields::Unnamed(_) => panic!("Struct representation not supported"),
130fad3a1d3Sopenharmony_ci    }
131fad3a1d3Sopenharmony_ci}
132fad3a1d3Sopenharmony_ci
133fad3a1d3Sopenharmony_cifn introspect_type(item: &syn::Type, lookup: &Lookup) -> types::Type {
134fad3a1d3Sopenharmony_ci    match item {
135fad3a1d3Sopenharmony_ci        syn::Type::Path(TypePath { qself: None, path }) => {
136fad3a1d3Sopenharmony_ci            let last = path.segments.last().unwrap();
137fad3a1d3Sopenharmony_ci            let string = last.ident.to_string();
138fad3a1d3Sopenharmony_ci
139fad3a1d3Sopenharmony_ci            match string.as_str() {
140fad3a1d3Sopenharmony_ci                "Option" => {
141fad3a1d3Sopenharmony_ci                    let nested = introspect_type(first_arg(&last.arguments), lookup);
142fad3a1d3Sopenharmony_ci                    types::Type::Option(Box::new(nested))
143fad3a1d3Sopenharmony_ci                }
144fad3a1d3Sopenharmony_ci                "Punctuated" => {
145fad3a1d3Sopenharmony_ci                    let nested = introspect_type(first_arg(&last.arguments), lookup);
146fad3a1d3Sopenharmony_ci                    let types::Type::Token(punct) =
147fad3a1d3Sopenharmony_ci                        introspect_type(last_arg(&last.arguments), lookup)
148fad3a1d3Sopenharmony_ci                    else {
149fad3a1d3Sopenharmony_ci                        panic!()
150fad3a1d3Sopenharmony_ci                    };
151fad3a1d3Sopenharmony_ci                    types::Type::Punctuated(types::Punctuated {
152fad3a1d3Sopenharmony_ci                        element: Box::new(nested),
153fad3a1d3Sopenharmony_ci                        punct,
154fad3a1d3Sopenharmony_ci                    })
155fad3a1d3Sopenharmony_ci                }
156fad3a1d3Sopenharmony_ci                "Vec" => {
157fad3a1d3Sopenharmony_ci                    let nested = introspect_type(first_arg(&last.arguments), lookup);
158fad3a1d3Sopenharmony_ci                    types::Type::Vec(Box::new(nested))
159fad3a1d3Sopenharmony_ci                }
160fad3a1d3Sopenharmony_ci                "Box" => {
161fad3a1d3Sopenharmony_ci                    let nested = introspect_type(first_arg(&last.arguments), lookup);
162fad3a1d3Sopenharmony_ci                    types::Type::Box(Box::new(nested))
163fad3a1d3Sopenharmony_ci                }
164fad3a1d3Sopenharmony_ci                "Brace" | "Bracket" | "Paren" | "Group" => types::Type::Group(string),
165fad3a1d3Sopenharmony_ci                "TokenStream" | "Literal" | "Ident" | "Span" => types::Type::Ext(string),
166fad3a1d3Sopenharmony_ci                "String" | "u32" | "usize" | "bool" => types::Type::Std(string),
167fad3a1d3Sopenharmony_ci                _ => {
168fad3a1d3Sopenharmony_ci                    let mut resolved = &last.ident;
169fad3a1d3Sopenharmony_ci                    while let Some(alias) = lookup.aliases.get(resolved) {
170fad3a1d3Sopenharmony_ci                        resolved = alias;
171fad3a1d3Sopenharmony_ci                    }
172fad3a1d3Sopenharmony_ci                    if lookup.items.get(resolved).is_some() {
173fad3a1d3Sopenharmony_ci                        types::Type::Syn(resolved.to_string())
174fad3a1d3Sopenharmony_ci                    } else {
175fad3a1d3Sopenharmony_ci                        unimplemented!("{}", resolved);
176fad3a1d3Sopenharmony_ci                    }
177fad3a1d3Sopenharmony_ci                }
178fad3a1d3Sopenharmony_ci            }
179fad3a1d3Sopenharmony_ci        }
180fad3a1d3Sopenharmony_ci        syn::Type::Tuple(TypeTuple { elems, .. }) => {
181fad3a1d3Sopenharmony_ci            let tys = elems.iter().map(|ty| introspect_type(ty, lookup)).collect();
182fad3a1d3Sopenharmony_ci            types::Type::Tuple(tys)
183fad3a1d3Sopenharmony_ci        }
184fad3a1d3Sopenharmony_ci        syn::Type::Macro(TypeMacro { mac })
185fad3a1d3Sopenharmony_ci            if mac.path.segments.last().unwrap().ident == "Token" =>
186fad3a1d3Sopenharmony_ci        {
187fad3a1d3Sopenharmony_ci            let content = mac.tokens.to_string();
188fad3a1d3Sopenharmony_ci            let ty = lookup.tokens.get(&content).unwrap().to_string();
189fad3a1d3Sopenharmony_ci
190fad3a1d3Sopenharmony_ci            types::Type::Token(ty)
191fad3a1d3Sopenharmony_ci        }
192fad3a1d3Sopenharmony_ci        _ => panic!("{}", quote!(#item).to_string()),
193fad3a1d3Sopenharmony_ci    }
194fad3a1d3Sopenharmony_ci}
195fad3a1d3Sopenharmony_ci
196fad3a1d3Sopenharmony_cifn introspect_features(attrs: &[Attribute]) -> types::Features {
197fad3a1d3Sopenharmony_ci    let mut ret = types::Features::default();
198fad3a1d3Sopenharmony_ci
199fad3a1d3Sopenharmony_ci    for attr in attrs {
200fad3a1d3Sopenharmony_ci        if !attr.path().is_ident("cfg") {
201fad3a1d3Sopenharmony_ci            continue;
202fad3a1d3Sopenharmony_ci        }
203fad3a1d3Sopenharmony_ci
204fad3a1d3Sopenharmony_ci        let features = attr.parse_args_with(parsing::parse_features).unwrap();
205fad3a1d3Sopenharmony_ci
206fad3a1d3Sopenharmony_ci        if ret.any.is_empty() {
207fad3a1d3Sopenharmony_ci            ret = features;
208fad3a1d3Sopenharmony_ci        } else if ret.any.len() < features.any.len() {
209fad3a1d3Sopenharmony_ci            assert!(ret.any.iter().all(|f| features.any.contains(f)));
210fad3a1d3Sopenharmony_ci        } else {
211fad3a1d3Sopenharmony_ci            assert!(features.any.iter().all(|f| ret.any.contains(f)));
212fad3a1d3Sopenharmony_ci            ret = features;
213fad3a1d3Sopenharmony_ci        }
214fad3a1d3Sopenharmony_ci    }
215fad3a1d3Sopenharmony_ci
216fad3a1d3Sopenharmony_ci    ret
217fad3a1d3Sopenharmony_ci}
218fad3a1d3Sopenharmony_ci
219fad3a1d3Sopenharmony_cifn is_pub(vis: &Visibility) -> bool {
220fad3a1d3Sopenharmony_ci    match vis {
221fad3a1d3Sopenharmony_ci        Visibility::Public(_) => true,
222fad3a1d3Sopenharmony_ci        _ => false,
223fad3a1d3Sopenharmony_ci    }
224fad3a1d3Sopenharmony_ci}
225fad3a1d3Sopenharmony_ci
226fad3a1d3Sopenharmony_cifn is_non_exhaustive(attrs: &[Attribute]) -> bool {
227fad3a1d3Sopenharmony_ci    for attr in attrs {
228fad3a1d3Sopenharmony_ci        if attr.path().is_ident("non_exhaustive") {
229fad3a1d3Sopenharmony_ci            return true;
230fad3a1d3Sopenharmony_ci        }
231fad3a1d3Sopenharmony_ci    }
232fad3a1d3Sopenharmony_ci    false
233fad3a1d3Sopenharmony_ci}
234fad3a1d3Sopenharmony_ci
235fad3a1d3Sopenharmony_cifn is_doc_hidden(attrs: &[Attribute]) -> bool {
236fad3a1d3Sopenharmony_ci    for attr in attrs {
237fad3a1d3Sopenharmony_ci        if attr.path().is_ident("doc") && attr.parse_args::<parsing::kw::hidden>().is_ok() {
238fad3a1d3Sopenharmony_ci            return true;
239fad3a1d3Sopenharmony_ci        }
240fad3a1d3Sopenharmony_ci    }
241fad3a1d3Sopenharmony_ci    false
242fad3a1d3Sopenharmony_ci}
243fad3a1d3Sopenharmony_ci
244fad3a1d3Sopenharmony_cifn first_arg(params: &PathArguments) -> &syn::Type {
245fad3a1d3Sopenharmony_ci    let data = match params {
246fad3a1d3Sopenharmony_ci        PathArguments::AngleBracketed(data) => data,
247fad3a1d3Sopenharmony_ci        _ => panic!("Expected at least 1 type argument here"),
248fad3a1d3Sopenharmony_ci    };
249fad3a1d3Sopenharmony_ci
250fad3a1d3Sopenharmony_ci    match data
251fad3a1d3Sopenharmony_ci        .args
252fad3a1d3Sopenharmony_ci        .first()
253fad3a1d3Sopenharmony_ci        .expect("Expected at least 1 type argument here")
254fad3a1d3Sopenharmony_ci    {
255fad3a1d3Sopenharmony_ci        GenericArgument::Type(ty) => ty,
256fad3a1d3Sopenharmony_ci        _ => panic!("Expected at least 1 type argument here"),
257fad3a1d3Sopenharmony_ci    }
258fad3a1d3Sopenharmony_ci}
259fad3a1d3Sopenharmony_ci
260fad3a1d3Sopenharmony_cifn last_arg(params: &PathArguments) -> &syn::Type {
261fad3a1d3Sopenharmony_ci    let data = match params {
262fad3a1d3Sopenharmony_ci        PathArguments::AngleBracketed(data) => data,
263fad3a1d3Sopenharmony_ci        _ => panic!("Expected at least 1 type argument here"),
264fad3a1d3Sopenharmony_ci    };
265fad3a1d3Sopenharmony_ci
266fad3a1d3Sopenharmony_ci    match data
267fad3a1d3Sopenharmony_ci        .args
268fad3a1d3Sopenharmony_ci        .last()
269fad3a1d3Sopenharmony_ci        .expect("Expected at least 1 type argument here")
270fad3a1d3Sopenharmony_ci    {
271fad3a1d3Sopenharmony_ci        GenericArgument::Type(ty) => ty,
272fad3a1d3Sopenharmony_ci        _ => panic!("Expected at least 1 type argument here"),
273fad3a1d3Sopenharmony_ci    }
274fad3a1d3Sopenharmony_ci}
275fad3a1d3Sopenharmony_ci
276fad3a1d3Sopenharmony_cimod parsing {
277fad3a1d3Sopenharmony_ci    use super::AstItem;
278fad3a1d3Sopenharmony_ci    use proc_macro2::TokenStream;
279fad3a1d3Sopenharmony_ci    use quote::quote;
280fad3a1d3Sopenharmony_ci    use std::collections::{BTreeMap, BTreeSet};
281fad3a1d3Sopenharmony_ci    use syn::parse::{ParseStream, Result};
282fad3a1d3Sopenharmony_ci    use syn::{
283fad3a1d3Sopenharmony_ci        braced, bracketed, parenthesized, parse_quote, token, Attribute, Expr, Ident, Lit, LitStr,
284fad3a1d3Sopenharmony_ci        Path, Token,
285fad3a1d3Sopenharmony_ci    };
286fad3a1d3Sopenharmony_ci    use syn_codegen as types;
287fad3a1d3Sopenharmony_ci
288fad3a1d3Sopenharmony_ci    fn peek_tag(input: ParseStream, tag: &str) -> bool {
289fad3a1d3Sopenharmony_ci        let ahead = input.fork();
290fad3a1d3Sopenharmony_ci        ahead.parse::<Token![#]>().is_ok()
291fad3a1d3Sopenharmony_ci            && ahead
292fad3a1d3Sopenharmony_ci                .parse::<Ident>()
293fad3a1d3Sopenharmony_ci                .map(|ident| ident == tag)
294fad3a1d3Sopenharmony_ci                .unwrap_or(false)
295fad3a1d3Sopenharmony_ci    }
296fad3a1d3Sopenharmony_ci
297fad3a1d3Sopenharmony_ci    // Parses #full - returns #[cfg(feature = "full")] if it is present, and
298fad3a1d3Sopenharmony_ci    // nothing otherwise.
299fad3a1d3Sopenharmony_ci    fn full(input: ParseStream) -> Vec<Attribute> {
300fad3a1d3Sopenharmony_ci        if peek_tag(input, "full") {
301fad3a1d3Sopenharmony_ci            input.parse::<Token![#]>().unwrap();
302fad3a1d3Sopenharmony_ci            input.parse::<Ident>().unwrap();
303fad3a1d3Sopenharmony_ci            vec![parse_quote!(#[cfg(feature = "full")])]
304fad3a1d3Sopenharmony_ci        } else {
305fad3a1d3Sopenharmony_ci            vec![]
306fad3a1d3Sopenharmony_ci        }
307fad3a1d3Sopenharmony_ci    }
308fad3a1d3Sopenharmony_ci
309fad3a1d3Sopenharmony_ci    // Parses a simple AstStruct without the `pub struct` prefix.
310fad3a1d3Sopenharmony_ci    fn ast_struct_inner(input: ParseStream) -> Result<AstItem> {
311fad3a1d3Sopenharmony_ci        let ident: Ident = input.parse()?;
312fad3a1d3Sopenharmony_ci        let features = full(input);
313fad3a1d3Sopenharmony_ci        let rest: TokenStream = input.parse()?;
314fad3a1d3Sopenharmony_ci        Ok(AstItem {
315fad3a1d3Sopenharmony_ci            ast: syn::parse2(quote! {
316fad3a1d3Sopenharmony_ci                pub struct #ident #rest
317fad3a1d3Sopenharmony_ci            })?,
318fad3a1d3Sopenharmony_ci            features,
319fad3a1d3Sopenharmony_ci        })
320fad3a1d3Sopenharmony_ci    }
321fad3a1d3Sopenharmony_ci
322fad3a1d3Sopenharmony_ci    pub fn ast_struct(input: ParseStream) -> Result<AstItem> {
323fad3a1d3Sopenharmony_ci        input.call(Attribute::parse_outer)?;
324fad3a1d3Sopenharmony_ci        input.parse::<Token![pub]>()?;
325fad3a1d3Sopenharmony_ci        input.parse::<Token![struct]>()?;
326fad3a1d3Sopenharmony_ci        let res = input.call(ast_struct_inner)?;
327fad3a1d3Sopenharmony_ci        Ok(res)
328fad3a1d3Sopenharmony_ci    }
329fad3a1d3Sopenharmony_ci
330fad3a1d3Sopenharmony_ci    pub fn ast_enum(input: ParseStream) -> Result<AstItem> {
331fad3a1d3Sopenharmony_ci        let attrs = input.call(Attribute::parse_outer)?;
332fad3a1d3Sopenharmony_ci        input.parse::<Token![pub]>()?;
333fad3a1d3Sopenharmony_ci        input.parse::<Token![enum]>()?;
334fad3a1d3Sopenharmony_ci        let ident: Ident = input.parse()?;
335fad3a1d3Sopenharmony_ci        let rest: TokenStream = input.parse()?;
336fad3a1d3Sopenharmony_ci        Ok(AstItem {
337fad3a1d3Sopenharmony_ci            ast: syn::parse2(quote! {
338fad3a1d3Sopenharmony_ci                #(#attrs)*
339fad3a1d3Sopenharmony_ci                pub enum #ident #rest
340fad3a1d3Sopenharmony_ci            })?,
341fad3a1d3Sopenharmony_ci            features: vec![],
342fad3a1d3Sopenharmony_ci        })
343fad3a1d3Sopenharmony_ci    }
344fad3a1d3Sopenharmony_ci
345fad3a1d3Sopenharmony_ci    // A single variant of an ast_enum_of_structs!
346fad3a1d3Sopenharmony_ci    struct EosVariant {
347fad3a1d3Sopenharmony_ci        attrs: Vec<Attribute>,
348fad3a1d3Sopenharmony_ci        name: Ident,
349fad3a1d3Sopenharmony_ci        member: Option<Path>,
350fad3a1d3Sopenharmony_ci    }
351fad3a1d3Sopenharmony_ci    fn eos_variant(input: ParseStream) -> Result<EosVariant> {
352fad3a1d3Sopenharmony_ci        let attrs = input.call(Attribute::parse_outer)?;
353fad3a1d3Sopenharmony_ci        let variant: Ident = input.parse()?;
354fad3a1d3Sopenharmony_ci        let member = if input.peek(token::Paren) {
355fad3a1d3Sopenharmony_ci            let content;
356fad3a1d3Sopenharmony_ci            parenthesized!(content in input);
357fad3a1d3Sopenharmony_ci            let path: Path = content.parse()?;
358fad3a1d3Sopenharmony_ci            Some(path)
359fad3a1d3Sopenharmony_ci        } else {
360fad3a1d3Sopenharmony_ci            None
361fad3a1d3Sopenharmony_ci        };
362fad3a1d3Sopenharmony_ci        input.parse::<Token![,]>()?;
363fad3a1d3Sopenharmony_ci        Ok(EosVariant {
364fad3a1d3Sopenharmony_ci            attrs,
365fad3a1d3Sopenharmony_ci            name: variant,
366fad3a1d3Sopenharmony_ci            member,
367fad3a1d3Sopenharmony_ci        })
368fad3a1d3Sopenharmony_ci    }
369fad3a1d3Sopenharmony_ci
370fad3a1d3Sopenharmony_ci    pub fn ast_enum_of_structs(input: ParseStream) -> Result<AstItem> {
371fad3a1d3Sopenharmony_ci        let attrs = input.call(Attribute::parse_outer)?;
372fad3a1d3Sopenharmony_ci        input.parse::<Token![pub]>()?;
373fad3a1d3Sopenharmony_ci        input.parse::<Token![enum]>()?;
374fad3a1d3Sopenharmony_ci        let ident: Ident = input.parse()?;
375fad3a1d3Sopenharmony_ci
376fad3a1d3Sopenharmony_ci        let content;
377fad3a1d3Sopenharmony_ci        braced!(content in input);
378fad3a1d3Sopenharmony_ci        let mut variants = Vec::new();
379fad3a1d3Sopenharmony_ci        while !content.is_empty() {
380fad3a1d3Sopenharmony_ci            variants.push(content.call(eos_variant)?);
381fad3a1d3Sopenharmony_ci        }
382fad3a1d3Sopenharmony_ci
383fad3a1d3Sopenharmony_ci        let enum_item = {
384fad3a1d3Sopenharmony_ci            let variants = variants.iter().map(|v| {
385fad3a1d3Sopenharmony_ci                let attrs = &v.attrs;
386fad3a1d3Sopenharmony_ci                let name = &v.name;
387fad3a1d3Sopenharmony_ci                if let Some(member) = &v.member {
388fad3a1d3Sopenharmony_ci                    quote!(#(#attrs)* #name(#member))
389fad3a1d3Sopenharmony_ci                } else {
390fad3a1d3Sopenharmony_ci                    quote!(#(#attrs)* #name)
391fad3a1d3Sopenharmony_ci                }
392fad3a1d3Sopenharmony_ci            });
393fad3a1d3Sopenharmony_ci            parse_quote! {
394fad3a1d3Sopenharmony_ci                #(#attrs)*
395fad3a1d3Sopenharmony_ci                pub enum #ident {
396fad3a1d3Sopenharmony_ci                    #(#variants),*
397fad3a1d3Sopenharmony_ci                }
398fad3a1d3Sopenharmony_ci            }
399fad3a1d3Sopenharmony_ci        };
400fad3a1d3Sopenharmony_ci        Ok(AstItem {
401fad3a1d3Sopenharmony_ci            ast: enum_item,
402fad3a1d3Sopenharmony_ci            features: vec![],
403fad3a1d3Sopenharmony_ci        })
404fad3a1d3Sopenharmony_ci    }
405fad3a1d3Sopenharmony_ci
406fad3a1d3Sopenharmony_ci    pub mod kw {
407fad3a1d3Sopenharmony_ci        syn::custom_keyword!(hidden);
408fad3a1d3Sopenharmony_ci        syn::custom_keyword!(macro_rules);
409fad3a1d3Sopenharmony_ci        syn::custom_keyword!(Token);
410fad3a1d3Sopenharmony_ci    }
411fad3a1d3Sopenharmony_ci
412fad3a1d3Sopenharmony_ci    pub fn parse_token_macro(input: ParseStream) -> Result<BTreeMap<String, String>> {
413fad3a1d3Sopenharmony_ci        let mut tokens = BTreeMap::new();
414fad3a1d3Sopenharmony_ci        while !input.is_empty() {
415fad3a1d3Sopenharmony_ci            let pattern;
416fad3a1d3Sopenharmony_ci            bracketed!(pattern in input);
417fad3a1d3Sopenharmony_ci            let token = pattern.parse::<TokenStream>()?.to_string();
418fad3a1d3Sopenharmony_ci            input.parse::<Token![=>]>()?;
419fad3a1d3Sopenharmony_ci            let expansion;
420fad3a1d3Sopenharmony_ci            braced!(expansion in input);
421fad3a1d3Sopenharmony_ci            input.parse::<Token![;]>()?;
422fad3a1d3Sopenharmony_ci            expansion.parse::<Token![$]>()?;
423fad3a1d3Sopenharmony_ci            let path: Path = expansion.parse()?;
424fad3a1d3Sopenharmony_ci            let ty = path.segments.last().unwrap().ident.to_string();
425fad3a1d3Sopenharmony_ci            tokens.insert(token, ty.to_string());
426fad3a1d3Sopenharmony_ci        }
427fad3a1d3Sopenharmony_ci        Ok(tokens)
428fad3a1d3Sopenharmony_ci    }
429fad3a1d3Sopenharmony_ci
430fad3a1d3Sopenharmony_ci    fn parse_feature(input: ParseStream) -> Result<String> {
431fad3a1d3Sopenharmony_ci        let i: Ident = input.parse()?;
432fad3a1d3Sopenharmony_ci        assert_eq!(i, "feature");
433fad3a1d3Sopenharmony_ci
434fad3a1d3Sopenharmony_ci        input.parse::<Token![=]>()?;
435fad3a1d3Sopenharmony_ci        let s = input.parse::<LitStr>()?;
436fad3a1d3Sopenharmony_ci
437fad3a1d3Sopenharmony_ci        Ok(s.value())
438fad3a1d3Sopenharmony_ci    }
439fad3a1d3Sopenharmony_ci
440fad3a1d3Sopenharmony_ci    pub fn parse_features(input: ParseStream) -> Result<types::Features> {
441fad3a1d3Sopenharmony_ci        let mut features = BTreeSet::new();
442fad3a1d3Sopenharmony_ci
443fad3a1d3Sopenharmony_ci        let i: Ident = input.fork().parse()?;
444fad3a1d3Sopenharmony_ci
445fad3a1d3Sopenharmony_ci        if i == "any" {
446fad3a1d3Sopenharmony_ci            input.parse::<Ident>()?;
447fad3a1d3Sopenharmony_ci
448fad3a1d3Sopenharmony_ci            let nested;
449fad3a1d3Sopenharmony_ci            parenthesized!(nested in input);
450fad3a1d3Sopenharmony_ci
451fad3a1d3Sopenharmony_ci            while !nested.is_empty() {
452fad3a1d3Sopenharmony_ci                features.insert(parse_feature(&nested)?);
453fad3a1d3Sopenharmony_ci
454fad3a1d3Sopenharmony_ci                if !nested.is_empty() {
455fad3a1d3Sopenharmony_ci                    nested.parse::<Token![,]>()?;
456fad3a1d3Sopenharmony_ci                }
457fad3a1d3Sopenharmony_ci            }
458fad3a1d3Sopenharmony_ci        } else if i == "feature" {
459fad3a1d3Sopenharmony_ci            features.insert(parse_feature(input)?);
460fad3a1d3Sopenharmony_ci            assert!(input.is_empty());
461fad3a1d3Sopenharmony_ci        } else {
462fad3a1d3Sopenharmony_ci            panic!("{:?}", i);
463fad3a1d3Sopenharmony_ci        }
464fad3a1d3Sopenharmony_ci
465fad3a1d3Sopenharmony_ci        Ok(types::Features { any: features })
466fad3a1d3Sopenharmony_ci    }
467fad3a1d3Sopenharmony_ci
468fad3a1d3Sopenharmony_ci    pub fn path_attr(attrs: &[Attribute]) -> Result<Option<&LitStr>> {
469fad3a1d3Sopenharmony_ci        for attr in attrs {
470fad3a1d3Sopenharmony_ci            if attr.path().is_ident("path") {
471fad3a1d3Sopenharmony_ci                if let Expr::Lit(expr) = &attr.meta.require_name_value()?.value {
472fad3a1d3Sopenharmony_ci                    if let Lit::Str(lit) = &expr.lit {
473fad3a1d3Sopenharmony_ci                        return Ok(Some(lit));
474fad3a1d3Sopenharmony_ci                    }
475fad3a1d3Sopenharmony_ci                }
476fad3a1d3Sopenharmony_ci            }
477fad3a1d3Sopenharmony_ci        }
478fad3a1d3Sopenharmony_ci        Ok(None)
479fad3a1d3Sopenharmony_ci    }
480fad3a1d3Sopenharmony_ci}
481fad3a1d3Sopenharmony_ci
482fad3a1d3Sopenharmony_cifn clone_features(features: &[Attribute]) -> Vec<Attribute> {
483fad3a1d3Sopenharmony_ci    features.iter().map(|attr| parse_quote!(#attr)).collect()
484fad3a1d3Sopenharmony_ci}
485fad3a1d3Sopenharmony_ci
486fad3a1d3Sopenharmony_cifn get_features(attrs: &[Attribute], base: &[Attribute]) -> Vec<Attribute> {
487fad3a1d3Sopenharmony_ci    let mut ret = clone_features(base);
488fad3a1d3Sopenharmony_ci
489fad3a1d3Sopenharmony_ci    for attr in attrs {
490fad3a1d3Sopenharmony_ci        if attr.path().is_ident("cfg") {
491fad3a1d3Sopenharmony_ci            ret.push(parse_quote!(#attr));
492fad3a1d3Sopenharmony_ci        }
493fad3a1d3Sopenharmony_ci    }
494fad3a1d3Sopenharmony_ci
495fad3a1d3Sopenharmony_ci    ret
496fad3a1d3Sopenharmony_ci}
497fad3a1d3Sopenharmony_ci
498fad3a1d3Sopenharmony_ci#[derive(Error, Debug)]
499fad3a1d3Sopenharmony_ci#[error("{path}:{line}:{column}: {error}")]
500fad3a1d3Sopenharmony_cistruct LoadFileError {
501fad3a1d3Sopenharmony_ci    path: PathBuf,
502fad3a1d3Sopenharmony_ci    line: usize,
503fad3a1d3Sopenharmony_ci    column: usize,
504fad3a1d3Sopenharmony_ci    error: Error,
505fad3a1d3Sopenharmony_ci}
506fad3a1d3Sopenharmony_ci
507fad3a1d3Sopenharmony_cifn load_file(
508fad3a1d3Sopenharmony_ci    relative_to_workspace_root: impl AsRef<Path>,
509fad3a1d3Sopenharmony_ci    features: &[Attribute],
510fad3a1d3Sopenharmony_ci    lookup: &mut Lookup,
511fad3a1d3Sopenharmony_ci) -> Result<()> {
512fad3a1d3Sopenharmony_ci    let error = match do_load_file(&relative_to_workspace_root, features, lookup).err() {
513fad3a1d3Sopenharmony_ci        None => return Ok(()),
514fad3a1d3Sopenharmony_ci        Some(error) => error,
515fad3a1d3Sopenharmony_ci    };
516fad3a1d3Sopenharmony_ci
517fad3a1d3Sopenharmony_ci    let error = error.downcast::<Error>()?;
518fad3a1d3Sopenharmony_ci    let span = error.span().start();
519fad3a1d3Sopenharmony_ci
520fad3a1d3Sopenharmony_ci    bail!(LoadFileError {
521fad3a1d3Sopenharmony_ci        path: relative_to_workspace_root.as_ref().to_owned(),
522fad3a1d3Sopenharmony_ci        line: span.line,
523fad3a1d3Sopenharmony_ci        column: span.column + 1,
524fad3a1d3Sopenharmony_ci        error,
525fad3a1d3Sopenharmony_ci    })
526fad3a1d3Sopenharmony_ci}
527fad3a1d3Sopenharmony_ci
528fad3a1d3Sopenharmony_cifn do_load_file(
529fad3a1d3Sopenharmony_ci    relative_to_workspace_root: impl AsRef<Path>,
530fad3a1d3Sopenharmony_ci    features: &[Attribute],
531fad3a1d3Sopenharmony_ci    lookup: &mut Lookup,
532fad3a1d3Sopenharmony_ci) -> Result<()> {
533fad3a1d3Sopenharmony_ci    let relative_to_workspace_root = relative_to_workspace_root.as_ref();
534fad3a1d3Sopenharmony_ci    let parent = relative_to_workspace_root.parent().expect("no parent path");
535fad3a1d3Sopenharmony_ci
536fad3a1d3Sopenharmony_ci    // Parse the file
537fad3a1d3Sopenharmony_ci    let src = fs::read_to_string(workspace_path::get(relative_to_workspace_root))?;
538fad3a1d3Sopenharmony_ci    let file = syn::parse_file(&src)?;
539fad3a1d3Sopenharmony_ci
540fad3a1d3Sopenharmony_ci    // Collect all of the interesting AstItems declared in this file or submodules.
541fad3a1d3Sopenharmony_ci    'items: for item in file.items {
542fad3a1d3Sopenharmony_ci        match item {
543fad3a1d3Sopenharmony_ci            Item::Mod(item) => {
544fad3a1d3Sopenharmony_ci                // Don't inspect inline modules.
545fad3a1d3Sopenharmony_ci                if item.content.is_some() {
546fad3a1d3Sopenharmony_ci                    continue;
547fad3a1d3Sopenharmony_ci                }
548fad3a1d3Sopenharmony_ci
549fad3a1d3Sopenharmony_ci                // We don't want to try to load the generated rust files and
550fad3a1d3Sopenharmony_ci                // parse them, so we ignore them here.
551fad3a1d3Sopenharmony_ci                for name in IGNORED_MODS {
552fad3a1d3Sopenharmony_ci                    if item.ident == name {
553fad3a1d3Sopenharmony_ci                        continue 'items;
554fad3a1d3Sopenharmony_ci                    }
555fad3a1d3Sopenharmony_ci                }
556fad3a1d3Sopenharmony_ci
557fad3a1d3Sopenharmony_ci                // Lookup any #[cfg()] attributes on the module and add them to
558fad3a1d3Sopenharmony_ci                // the feature set.
559fad3a1d3Sopenharmony_ci                //
560fad3a1d3Sopenharmony_ci                // The derive module is weird because it is built with either
561fad3a1d3Sopenharmony_ci                // `full` or `derive` but exported only under `derive`.
562fad3a1d3Sopenharmony_ci                let features = if item.ident == "derive" {
563fad3a1d3Sopenharmony_ci                    vec![parse_quote!(#[cfg(feature = "derive")])]
564fad3a1d3Sopenharmony_ci                } else {
565fad3a1d3Sopenharmony_ci                    get_features(&item.attrs, features)
566fad3a1d3Sopenharmony_ci                };
567fad3a1d3Sopenharmony_ci
568fad3a1d3Sopenharmony_ci                // Look up the submodule file, and recursively parse it.
569fad3a1d3Sopenharmony_ci                // Only handles same-directory .rs file submodules for now.
570fad3a1d3Sopenharmony_ci                let filename = if let Some(filename) = parsing::path_attr(&item.attrs)? {
571fad3a1d3Sopenharmony_ci                    filename.value()
572fad3a1d3Sopenharmony_ci                } else {
573fad3a1d3Sopenharmony_ci                    format!("{}.rs", item.ident)
574fad3a1d3Sopenharmony_ci                };
575fad3a1d3Sopenharmony_ci                let path = parent.join(filename);
576fad3a1d3Sopenharmony_ci                load_file(path, &features, lookup)?;
577fad3a1d3Sopenharmony_ci            }
578fad3a1d3Sopenharmony_ci            Item::Macro(item) => {
579fad3a1d3Sopenharmony_ci                // Lookip any #[cfg()] attributes directly on the macro
580fad3a1d3Sopenharmony_ci                // invocation, and add them to the feature set.
581fad3a1d3Sopenharmony_ci                let features = get_features(&item.attrs, features);
582fad3a1d3Sopenharmony_ci
583fad3a1d3Sopenharmony_ci                // Try to parse the AstItem declaration out of the item.
584fad3a1d3Sopenharmony_ci                let tts = item.mac.tokens.clone();
585fad3a1d3Sopenharmony_ci                let mut found = if item.mac.path.is_ident("ast_struct") {
586fad3a1d3Sopenharmony_ci                    parsing::ast_struct.parse2(tts)
587fad3a1d3Sopenharmony_ci                } else if item.mac.path.is_ident("ast_enum") {
588fad3a1d3Sopenharmony_ci                    parsing::ast_enum.parse2(tts)
589fad3a1d3Sopenharmony_ci                } else if item.mac.path.is_ident("ast_enum_of_structs") {
590fad3a1d3Sopenharmony_ci                    parsing::ast_enum_of_structs.parse2(tts)
591fad3a1d3Sopenharmony_ci                } else {
592fad3a1d3Sopenharmony_ci                    continue;
593fad3a1d3Sopenharmony_ci                }?;
594fad3a1d3Sopenharmony_ci
595fad3a1d3Sopenharmony_ci                // Record our features on the parsed AstItems.
596fad3a1d3Sopenharmony_ci                found.features.extend(clone_features(&features));
597fad3a1d3Sopenharmony_ci                lookup.items.insert(found.ast.ident.clone(), found);
598fad3a1d3Sopenharmony_ci            }
599fad3a1d3Sopenharmony_ci            Item::Struct(item) => {
600fad3a1d3Sopenharmony_ci                let ident = item.ident;
601fad3a1d3Sopenharmony_ci                if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
602fad3a1d3Sopenharmony_ci                    lookup.items.insert(
603fad3a1d3Sopenharmony_ci                        ident.clone(),
604fad3a1d3Sopenharmony_ci                        AstItem {
605fad3a1d3Sopenharmony_ci                            ast: DeriveInput {
606fad3a1d3Sopenharmony_ci                                ident,
607fad3a1d3Sopenharmony_ci                                vis: item.vis,
608fad3a1d3Sopenharmony_ci                                attrs: item.attrs,
609fad3a1d3Sopenharmony_ci                                generics: item.generics,
610fad3a1d3Sopenharmony_ci                                data: Data::Struct(DataStruct {
611fad3a1d3Sopenharmony_ci                                    fields: item.fields,
612fad3a1d3Sopenharmony_ci                                    struct_token: item.struct_token,
613fad3a1d3Sopenharmony_ci                                    semi_token: item.semi_token,
614fad3a1d3Sopenharmony_ci                                }),
615fad3a1d3Sopenharmony_ci                            },
616fad3a1d3Sopenharmony_ci                            features: clone_features(features),
617fad3a1d3Sopenharmony_ci                        },
618fad3a1d3Sopenharmony_ci                    );
619fad3a1d3Sopenharmony_ci                }
620fad3a1d3Sopenharmony_ci            }
621fad3a1d3Sopenharmony_ci            Item::Use(item)
622fad3a1d3Sopenharmony_ci                if relative_to_workspace_root == Path::new(SYN_CRATE_ROOT)
623fad3a1d3Sopenharmony_ci                    && matches!(item.vis, Visibility::Public(_)) =>
624fad3a1d3Sopenharmony_ci            {
625fad3a1d3Sopenharmony_ci                load_aliases(item.tree, lookup);
626fad3a1d3Sopenharmony_ci            }
627fad3a1d3Sopenharmony_ci            _ => {}
628fad3a1d3Sopenharmony_ci        }
629fad3a1d3Sopenharmony_ci    }
630fad3a1d3Sopenharmony_ci    Ok(())
631fad3a1d3Sopenharmony_ci}
632fad3a1d3Sopenharmony_ci
633fad3a1d3Sopenharmony_cifn load_aliases(use_tree: UseTree, lookup: &mut Lookup) {
634fad3a1d3Sopenharmony_ci    match use_tree {
635fad3a1d3Sopenharmony_ci        UseTree::Path(use_tree) => load_aliases(*use_tree.tree, lookup),
636fad3a1d3Sopenharmony_ci        UseTree::Rename(use_tree) => {
637fad3a1d3Sopenharmony_ci            lookup.aliases.insert(use_tree.rename, use_tree.ident);
638fad3a1d3Sopenharmony_ci        }
639fad3a1d3Sopenharmony_ci        UseTree::Group(use_tree) => {
640fad3a1d3Sopenharmony_ci            for use_tree in use_tree.items {
641fad3a1d3Sopenharmony_ci                load_aliases(use_tree, lookup);
642fad3a1d3Sopenharmony_ci            }
643fad3a1d3Sopenharmony_ci        }
644fad3a1d3Sopenharmony_ci        UseTree::Name(_) | UseTree::Glob(_) => {}
645fad3a1d3Sopenharmony_ci    }
646fad3a1d3Sopenharmony_ci}
647fad3a1d3Sopenharmony_ci
648fad3a1d3Sopenharmony_cifn load_token_file(
649fad3a1d3Sopenharmony_ci    relative_to_workspace_root: impl AsRef<Path>,
650fad3a1d3Sopenharmony_ci) -> Result<BTreeMap<String, String>> {
651fad3a1d3Sopenharmony_ci    let path = workspace_path::get(relative_to_workspace_root);
652fad3a1d3Sopenharmony_ci    let src = fs::read_to_string(path)?;
653fad3a1d3Sopenharmony_ci    let file = syn::parse_file(&src)?;
654fad3a1d3Sopenharmony_ci    for item in file.items {
655fad3a1d3Sopenharmony_ci        if let Item::Macro(item) = item {
656fad3a1d3Sopenharmony_ci            match item.ident {
657fad3a1d3Sopenharmony_ci                Some(i) if i == "Token" => {}
658fad3a1d3Sopenharmony_ci                _ => continue,
659fad3a1d3Sopenharmony_ci            }
660fad3a1d3Sopenharmony_ci            let tokens = item.mac.parse_body_with(parsing::parse_token_macro)?;
661fad3a1d3Sopenharmony_ci            return Ok(tokens);
662fad3a1d3Sopenharmony_ci        }
663fad3a1d3Sopenharmony_ci    }
664fad3a1d3Sopenharmony_ci
665fad3a1d3Sopenharmony_ci    panic!("failed to parse Token macro")
666fad3a1d3Sopenharmony_ci}
667