blob: b43bef539cea36ebe4ddfb708f988ab4e31e92c9 [file] [log] [blame]
Carl Lerche058ff472019-02-13 16:23:52 -08001use crate::types;
2
David Tolnay14d463e2019-02-15 14:23:51 -08003use indexmap::IndexMap;
Carl Lerche058ff472019-02-13 16:23:52 -08004use syn::{Data, DataStruct, DeriveInput, Ident, Item};
5
6use std::collections::BTreeMap;
7use std::fs::File;
8use std::io::Read;
9use std::path::Path;
10
11const SYN_CRATE_ROOT: &str = "../src/lib.rs";
12const TOKEN_SRC: &str = "../src/token.rs";
13const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
14const EXTRA_TYPES: &[&str] = &["Lifetime"];
Carl Lerche058ff472019-02-13 16:23:52 -080015
16// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
17type ItemLookup = BTreeMap<Ident, AstItem>;
18type TokenLookup = BTreeMap<String, String>;
19
20/// Parse the contents of `src` and return a list of AST types.
David Tolnayf9bb8ff2019-02-15 13:10:14 -080021pub fn parse() -> types::Definitions {
Carl Lerche058ff472019-02-13 16:23:52 -080022 let mut item_lookup = BTreeMap::new();
23 load_file(SYN_CRATE_ROOT, &[], &mut item_lookup).unwrap();
24
25 let token_lookup = load_token_file(TOKEN_SRC).unwrap();
26
David Tolnayf9bb8ff2019-02-15 13:10:14 -080027 let types = item_lookup
Carl Lerche058ff472019-02-13 16:23:52 -080028 .values()
29 .map(|item| introspect_item(item, &item_lookup, &token_lookup))
David Tolnayf9bb8ff2019-02-15 13:10:14 -080030 .collect();
31
32 let tokens = token_lookup.into_iter().map(|(name, ty)| (ty, name)).collect();
33
34 types::Definitions { types, tokens }
Carl Lerche058ff472019-02-13 16:23:52 -080035}
36
37/// Data extracted from syn source
38#[derive(Clone)]
39pub struct AstItem {
40 ast: DeriveInput,
41 features: Vec<syn::Attribute>,
42}
43
David Tolnayf9bb8ff2019-02-15 13:10:14 -080044fn introspect_item(item: &AstItem, items: &ItemLookup, tokens: &TokenLookup) -> types::Node {
Carl Lerche058ff472019-02-13 16:23:52 -080045 let features = introspect_features(&item.features);
46
47 match &item.ast.data {
David Tolnayf9bb8ff2019-02-15 13:10:14 -080048 Data::Enum(ref data) => types::Node::Enum(introspect_enum(
Carl Lerche058ff472019-02-13 16:23:52 -080049 &item.ast.ident,
50 features,
51 data,
52 items,
53 tokens,
54 )),
David Tolnayf9bb8ff2019-02-15 13:10:14 -080055 Data::Struct(ref data) => types::Node::Struct(introspect_struct(
Carl Lerche058ff472019-02-13 16:23:52 -080056 &item.ast.ident,
57 features,
58 data,
59 items,
60 tokens,
61 )),
62 Data::Union(..) => panic!("Union not supported"),
63 }
64}
65
66fn introspect_enum(
67 ident: &Ident,
68 features: types::Features,
69 item: &syn::DataEnum,
70 items: &ItemLookup,
71 tokens: &TokenLookup,
72) -> types::Enum {
73 let variants = item
74 .variants
75 .iter()
76 .map(|variant| {
77 let fields = match &variant.fields {
78 syn::Fields::Unnamed(fields) => fields
79 .unnamed
80 .iter()
81 .map(|field| introspect_type(&field.ty, items, tokens))
82 .collect(),
83 syn::Fields::Unit => vec![],
84 _ => panic!("Enum representation not supported"),
85 };
86
87 types::Variant::new(variant.ident.to_string(), fields)
88 })
89 .collect();
90
91 types::Enum::new(ident.to_string(), features, variants)
92}
93
94fn introspect_struct(
95 ident: &Ident,
96 features: types::Features,
97 item: &syn::DataStruct,
98 items: &ItemLookup,
99 tokens: &TokenLookup,
100) -> types::Struct {
David Tolnay8964fff2019-02-15 14:34:51 -0800101 let all_fields_pub = item.fields.iter().all(|field| is_pub(&field.vis));
102 if !all_fields_pub {
103 return types::Struct::new(ident.to_string(), features, IndexMap::new());
104 }
105
Carl Lerche058ff472019-02-13 16:23:52 -0800106 let fields = match &item.fields {
107 syn::Fields::Named(fields) => fields
108 .named
109 .iter()
110 .map(|field| {
David Tolnay14d463e2019-02-15 14:23:51 -0800111 (
Carl Lerche058ff472019-02-13 16:23:52 -0800112 field.ident.as_ref().unwrap().to_string(),
113 introspect_type(&field.ty, items, tokens),
114 )
115 })
116 .collect(),
David Tolnay14d463e2019-02-15 14:23:51 -0800117 syn::Fields::Unit => IndexMap::new(),
Carl Lerche058ff472019-02-13 16:23:52 -0800118 _ => panic!("Struct representation not supported"),
119 };
120
David Tolnay8964fff2019-02-15 14:34:51 -0800121 types::Struct::new(ident.to_string(), features, fields)
Carl Lerche058ff472019-02-13 16:23:52 -0800122}
123
124fn introspect_type(item: &syn::Type, items: &ItemLookup, tokens: &TokenLookup) -> types::Type {
125 match item {
126 syn::Type::Path(syn::TypePath {
127 qself: None,
128 ref path,
129 }) => {
130 let last = path.segments.last().unwrap().into_value();
131
132 match &last.ident.to_string()[..] {
133 "Option" => {
134 let nested = introspect_type(first_arg(&last.arguments), items, tokens);
135 types::Type::Option(Box::new(nested))
136 }
137 "Punctuated" => {
138 let nested = introspect_type(first_arg(&last.arguments), items, tokens);
139 let punct = match introspect_type(last_arg(&last.arguments), items, tokens) {
140 types::Type::Token(s) => s,
141 _ => panic!(),
142 };
143
David Tolnay295141b2019-02-15 12:45:33 -0800144 types::Type::Punctuated(types::Punctuated::new(nested, punct))
Carl Lerche058ff472019-02-13 16:23:52 -0800145 }
146 "Vec" => {
147 let nested = introspect_type(first_arg(&last.arguments), items, tokens);
148 types::Type::Vec(Box::new(nested))
149 }
150 "Box" => {
151 let nested = introspect_type(first_arg(&last.arguments), items, tokens);
152 types::Type::Box(Box::new(nested))
153 }
154 "Brace" | "Bracket" | "Paren" | "Group" => {
David Tolnay295141b2019-02-15 12:45:33 -0800155 types::Type::Group(last.ident.to_string())
Carl Lerche058ff472019-02-13 16:23:52 -0800156 }
Carl Lerchecbf7cc12019-02-15 14:09:31 -0800157 "TokenStream" | "Literal" | "Ident" | "Span" => types::Type::Ext(last.ident.to_string()),
Carl Lerche058ff472019-02-13 16:23:52 -0800158 "String" | "u32" | "usize" | "bool" => types::Type::Std(last.ident.to_string()),
159 _ => {
160 if items.get(&last.ident).is_some() {
David Tolnayd3076572019-02-15 13:32:44 -0800161 types::Type::Syn(last.ident.to_string())
Carl Lerche058ff472019-02-13 16:23:52 -0800162 } else {
163 unimplemented!("{}", last.ident.to_string());
164 }
165 }
166 }
167 }
168 syn::Type::Tuple(syn::TypeTuple { ref elems, .. }) => {
169 let tys = elems
170 .iter()
171 .map(|ty| introspect_type(&ty, items, tokens))
172 .collect();
173 types::Type::Tuple(tys)
174 }
175 syn::Type::Macro(syn::TypeMacro { ref mac })
176 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
177 {
178 let content = mac.tts.to_string();
179 let ty = tokens.get(&content).unwrap().to_string();
180
David Tolnay157c7eb2019-02-15 13:21:48 -0800181 types::Type::Token(ty)
Carl Lerche058ff472019-02-13 16:23:52 -0800182 }
183 _ => panic!("{}", quote!(#item).to_string()),
184 }
185}
186
187fn introspect_features(attrs: &[syn::Attribute]) -> types::Features {
188 let mut ret = types::Features::default();
189
190 for attr in attrs {
191 if !attr.path.is_ident("cfg") {
192 continue;
193 }
194
195 let features: types::Features = syn::parse2(attr.tts.clone()).unwrap();
196 ret.join(&features);
197 }
198
199 ret
200}
201
202fn is_pub(vis: &syn::Visibility) -> bool {
203 match vis {
204 syn::Visibility::Public(_) => true,
205 _ => false,
206 }
207}
208
209fn first_arg(params: &syn::PathArguments) -> &syn::Type {
210 let data = match *params {
211 syn::PathArguments::AngleBracketed(ref data) => data,
212 _ => panic!("Expected at least 1 type argument here"),
213 };
214
215 match **data
216 .args
217 .first()
218 .expect("Expected at least 1 type argument here")
219 .value()
220 {
221 syn::GenericArgument::Type(ref ty) => ty,
222 _ => panic!("Expected at least 1 type argument here"),
223 }
224}
225
226fn last_arg(params: &syn::PathArguments) -> &syn::Type {
227 let data = match *params {
228 syn::PathArguments::AngleBracketed(ref data) => data,
229 _ => panic!("Expected at least 1 type argument here"),
230 };
231
232 match **data
233 .args
234 .last()
235 .expect("Expected at least 1 type argument here")
236 .value()
237 {
238 syn::GenericArgument::Type(ref ty) => ty,
239 _ => panic!("Expected at least 1 type argument here"),
240 }
241}
242
243mod parsing {
244 use super::{AstItem, TokenLookup};
245 use crate::types;
246
247 use proc_macro2::TokenStream;
248 use syn;
249 use syn::parse::{Parse, ParseStream, Result};
250 use syn::*;
251
252 use std::collections::BTreeMap;
253
254 fn peek_tag(input: ParseStream, tag: &str) -> bool {
255 let ahead = input.fork();
256 ahead.parse::<Token![#]>().is_ok()
257 && ahead
258 .parse::<Ident>()
259 .map(|ident| ident == tag)
260 .unwrap_or(false)
261 }
262
263 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
264 // nothing otherwise.
265 fn full(input: ParseStream) -> Vec<syn::Attribute> {
266 if peek_tag(input, "full") {
267 input.parse::<Token![#]>().unwrap();
268 input.parse::<Ident>().unwrap();
269 vec![parse_quote!(#[cfg(feature = "full")])]
270 } else {
271 vec![]
272 }
273 }
274
275 fn skip_manual_extra_traits(input: ParseStream) {
276 if peek_tag(input, "manual_extra_traits") {
277 input.parse::<Token![#]>().unwrap();
278 input.parse::<Ident>().unwrap();
279 }
280 }
281
282 // Parses a simple AstStruct without the `pub struct` prefix.
283 fn ast_struct_inner(input: ParseStream) -> Result<AstItem> {
284 let ident: Ident = input.parse()?;
285 let features = full(input);
286 skip_manual_extra_traits(input);
287 let rest: TokenStream = input.parse()?;
288 Ok(AstItem {
289 ast: syn::parse2(quote! {
290 pub struct #ident #rest
291 })?,
292 features,
293 })
294 }
295
296 // ast_struct! parsing
297 pub struct AstStruct(pub(super) Vec<AstItem>);
298 impl Parse for AstStruct {
299 fn parse(input: ParseStream) -> Result<Self> {
300 input.call(Attribute::parse_outer)?;
301 input.parse::<Token![pub]>()?;
302 input.parse::<Token![struct]>()?;
303 let res = input.call(ast_struct_inner)?;
304 Ok(AstStruct(vec![res]))
305 }
306 }
307
308 fn no_visit(input: ParseStream) -> bool {
309 if peek_tag(input, "no_visit") {
310 input.parse::<Token![#]>().unwrap();
311 input.parse::<Ident>().unwrap();
312 true
313 } else {
314 false
315 }
316 }
317
318 // ast_enum! parsing
319 pub struct AstEnum(pub Vec<AstItem>);
320 impl Parse for AstEnum {
321 fn parse(input: ParseStream) -> Result<Self> {
322 input.call(Attribute::parse_outer)?;
323 input.parse::<Token![pub]>()?;
324 input.parse::<Token![enum]>()?;
325 let ident: Ident = input.parse()?;
326 let no_visit = no_visit(input);
327 let rest: TokenStream = input.parse()?;
328 Ok(AstEnum(if no_visit {
329 vec![]
330 } else {
331 vec![AstItem {
332 ast: syn::parse2(quote! {
333 pub enum #ident #rest
334 })?,
335 features: vec![],
336 }]
337 }))
338 }
339 }
340
341 // A single variant of an ast_enum_of_structs!
342 struct EosVariant {
343 name: Ident,
344 member: Option<Path>,
345 inner: Option<AstItem>,
346 }
347 fn eos_variant(input: ParseStream) -> Result<EosVariant> {
348 input.call(Attribute::parse_outer)?;
349 input.parse::<Token![pub]>()?;
350 let variant: Ident = input.parse()?;
351 let (member, inner) = if input.peek(token::Paren) {
352 let content;
353 parenthesized!(content in input);
354 if content.fork().call(ast_struct_inner).is_ok() {
355 let item = content.call(ast_struct_inner)?;
356 (Some(Path::from(item.ast.ident.clone())), Some(item))
357 } else {
358 let path: Path = content.parse()?;
359 (Some(path), None)
360 }
361 } else {
362 (None, None)
363 };
364 input.parse::<Token![,]>()?;
365 Ok(EosVariant {
366 name: variant,
367 member,
368 inner,
369 })
370 }
371
372 // ast_enum_of_structs! parsing
373 pub struct AstEnumOfStructs(pub Vec<AstItem>);
374 impl Parse for AstEnumOfStructs {
375 fn parse(input: ParseStream) -> Result<Self> {
376 input.call(Attribute::parse_outer)?;
377 input.parse::<Token![pub]>()?;
378 input.parse::<Token![enum]>()?;
379 let ident: Ident = input.parse()?;
380
381 let content;
382 braced!(content in input);
383 let mut variants = Vec::new();
384 while !content.is_empty() {
385 variants.push(content.call(eos_variant)?);
386 }
387
388 if let Some(ident) = input.parse::<Option<Ident>>()? {
389 assert_eq!(ident, "do_not_generate_to_tokens");
390 }
391
392 let enum_item = {
393 let variants = variants.iter().map(|v| {
394 let name = v.name.clone();
395 match v.member {
396 Some(ref member) => quote!(#name(#member)),
397 None => quote!(#name),
398 }
399 });
400 parse_quote! {
401 pub enum #ident {
402 #(#variants),*
403 }
404 }
405 };
406 let mut items = vec![AstItem {
407 ast: enum_item,
408 features: vec![],
409 }];
410 items.extend(variants.into_iter().filter_map(|v| v.inner));
411 Ok(AstEnumOfStructs(items))
412 }
413 }
414
415 pub struct TokenMacro(pub TokenLookup);
416 impl Parse for TokenMacro {
417 fn parse(input: ParseStream) -> Result<Self> {
418 let mut tokens = BTreeMap::new();
419 while !input.is_empty() {
420 let content;
421 parenthesized!(content in input);
422 let token = content.parse::<TokenStream>()?.to_string();
423 input.parse::<Token![=]>()?;
424 input.parse::<Token![>]>()?;
425 let content;
426 braced!(content in input);
427 input.parse::<Token![;]>()?;
428 content.parse::<token::Dollar>()?;
429 let path: Path = content.parse()?;
430 let ty = path.segments.last().unwrap().into_value().ident.to_string();
431 tokens.insert(token, ty.to_string());
432 }
433 Ok(TokenMacro(tokens))
434 }
435 }
436
437 fn parse_feature(input: ParseStream) -> Result<String> {
438 let i: syn::Ident = input.parse()?;
439 assert_eq!(i, "feature");
440
441 input.parse::<Token![=]>()?;
442 let s = input.parse::<syn::LitStr>()?;
443
444 Ok(s.value())
445 }
446
447 impl Parse for types::Features {
448 fn parse(input: ParseStream) -> Result<Self> {
449 let mut features = vec![];
450
451 let level_1;
452 parenthesized!(level_1 in input);
453
454 let i: syn::Ident = level_1.fork().parse()?;
455
456 if i == "any" {
457 level_1.parse::<syn::Ident>()?;
458
459 let level_2;
460 parenthesized!(level_2 in level_1);
461
462 while !level_2.is_empty() {
463 features.push(parse_feature(&level_2)?);
464
465 if !level_2.is_empty() {
466 level_2.parse::<Token![,]>()?;
467 }
468 }
469 } else if i == "feature" {
470 features.push(parse_feature(&level_1)?);
471 assert!(level_1.is_empty());
472 } else {
473 panic!("{:?}", i);
474 }
475
476 assert!(input.is_empty());
477
478 Ok(types::Features::new(features))
479 }
480 }
481}
482
483fn get_features(attrs: &[syn::Attribute], base: &[syn::Attribute]) -> Vec<syn::Attribute> {
484 let mut ret = base.to_owned();
485
486 for attr in attrs {
487 if attr.path.is_ident("cfg") {
488 ret.push(attr.clone());
489 }
490 }
491
492 ret
493}
494
495type Error = Box<::std::error::Error>;
496
497fn load_file<P: AsRef<Path>>(
498 name: P,
499 features: &[syn::Attribute],
500 lookup: &mut ItemLookup,
501) -> Result<(), Error> {
502 let name = name.as_ref();
503 let parent = name.parent().ok_or("no parent path")?;
504
505 let mut f = File::open(name)?;
506 let mut src = String::new();
507 f.read_to_string(&mut src)?;
508
509 // Parse the file
510 let file = syn::parse_file(&src)?;
511
512 // Collect all of the interesting AstItems declared in this file or submodules.
513 'items: for item in file.items {
514 match item {
515 Item::Mod(item) => {
516 // Don't inspect inline modules.
517 if item.content.is_some() {
518 continue;
519 }
520
521 // We don't want to try to load the generated rust files and
522 // parse them, so we ignore them here.
523 for name in IGNORED_MODS {
524 if item.ident == name {
525 continue 'items;
526 }
527 }
528
529 // Lookup any #[cfg()] attributes on the module and add them to
530 // the feature set.
531 //
532 // The derive module is weird because it is built with either
533 // `full` or `derive` but exported only under `derive`.
534 let features = if item.ident == "derive" {
535 vec![parse_quote!(#[cfg(feature = "derive")])]
536 } else {
537 get_features(&item.attrs, features)
538 };
539
540 // Look up the submodule file, and recursively parse it.
541 // XXX: Only handles same-directory .rs file submodules.
542 let path = parent.join(&format!("{}.rs", item.ident));
543 load_file(path, &features, lookup)?;
544 }
545 Item::Macro(item) => {
546 // Lookip any #[cfg()] attributes directly on the macro
547 // invocation, and add them to the feature set.
548 let features = get_features(&item.attrs, features);
549
550 // Try to parse the AstItem declaration out of the item.
551 let tts = &item.mac.tts;
552 let found = if item.mac.path.is_ident("ast_struct") {
553 syn::parse2::<parsing::AstStruct>(quote!(#tts))?.0
554 } else if item.mac.path.is_ident("ast_enum") {
555 syn::parse2::<parsing::AstEnum>(quote!(#tts))?.0
556 } else if item.mac.path.is_ident("ast_enum_of_structs") {
557 syn::parse2::<parsing::AstEnumOfStructs>(quote!(#tts))?.0
558 } else {
559 continue;
560 };
561
562 // Record our features on the parsed AstItems.
563 for mut item in found {
564 item.features.extend(features.clone());
565 lookup.insert(item.ast.ident.clone(), item);
566 }
567 }
568 Item::Struct(item) => {
569 let ident = item.ident;
570 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
571 lookup.insert(
572 ident.clone(),
573 AstItem {
574 ast: DeriveInput {
575 ident,
576 vis: item.vis,
577 attrs: item.attrs,
578 generics: item.generics,
579 data: Data::Struct(DataStruct {
580 fields: item.fields,
581 struct_token: item.struct_token,
582 semi_token: item.semi_token,
583 }),
584 },
585 features: features.to_owned(),
586 },
587 );
588 }
589 }
590 _ => {}
591 }
592 }
593 Ok(())
594}
595
596fn load_token_file<P: AsRef<Path>>(name: P) -> Result<TokenLookup, Error> {
597 let name = name.as_ref();
598 let mut f = File::open(name)?;
599 let mut src = String::new();
600 f.read_to_string(&mut src)?;
601 let file = syn::parse_file(&src)?;
602 for item in file.items {
603 match item {
604 Item::Macro(item) => {
605 match item.ident {
606 Some(ref i) if i == "Token" => {}
607 _ => continue,
608 }
609 let tts = &item.mac.tts;
610 let tokens = syn::parse2::<parsing::TokenMacro>(quote!(#tts))?.0;
611 return Ok(tokens);
612 }
613 _ => {}
614 }
615 }
616
617 Err("failed to parse Token macro".into())
618}