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