diff options
author | Dmitry <[email protected]> | 2020-08-09 14:35:51 +0100 |
---|---|---|
committer | Dmitry <[email protected]> | 2020-08-09 14:39:32 +0100 |
commit | 8068302fefc75440b823f4bf1731a5f347d7c767 (patch) | |
tree | 251b967182e79bc82a58c2fb208c688f6152df1f /xtask/src/codegen | |
parent | 1a43a0f63e0008787225abb6fb2baef97b6a39e0 (diff) | |
parent | 8a57afe5a4bfab40072a83f7dc4ca560bf860919 (diff) |
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'xtask/src/codegen')
-rw-r--r-- | xtask/src/codegen/gen_syntax.rs | 353 | ||||
-rw-r--r-- | xtask/src/codegen/gen_unstable_future_descriptor.rs | 2 | ||||
-rw-r--r-- | xtask/src/codegen/rust.ungram | 587 |
3 files changed, 897 insertions, 45 deletions
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs index 745a25862..cafad8070 100644 --- a/xtask/src/codegen/gen_syntax.rs +++ b/xtask/src/codegen/gen_syntax.rs | |||
@@ -3,34 +3,41 @@ | |||
3 | //! Specifically, it generates the `SyntaxKind` enum and a number of newtype | 3 | //! Specifically, it generates the `SyntaxKind` enum and a number of newtype |
4 | //! wrappers around `SyntaxNode` which implement `ra_syntax::AstNode`. | 4 | //! wrappers around `SyntaxNode` which implement `ra_syntax::AstNode`. |
5 | 5 | ||
6 | use std::{collections::HashSet, fmt::Write}; | 6 | use std::{ |
7 | collections::{BTreeSet, HashSet}, | ||
8 | fmt::Write, | ||
9 | }; | ||
7 | 10 | ||
8 | use proc_macro2::{Punct, Spacing}; | 11 | use proc_macro2::{Punct, Spacing}; |
9 | use quote::{format_ident, quote}; | 12 | use quote::{format_ident, quote}; |
13 | use ungrammar::{rust_grammar, Grammar, Rule}; | ||
10 | 14 | ||
11 | use crate::{ | 15 | use crate::{ |
12 | ast_src::{AstSrc, Field, FieldSrc, KindsSrc, AST_SRC, KINDS_SRC}, | 16 | ast_src::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field, KindsSrc, KINDS_SRC}, |
13 | codegen::{self, update, Mode}, | 17 | codegen::{self, update, Mode}, |
14 | project_root, Result, | 18 | project_root, Result, |
15 | }; | 19 | }; |
16 | 20 | ||
17 | pub fn generate_syntax(mode: Mode) -> Result<()> { | 21 | pub fn generate_syntax(mode: Mode) -> Result<()> { |
22 | let grammar = rust_grammar(); | ||
23 | let ast = lower(&grammar); | ||
24 | |||
18 | let syntax_kinds_file = project_root().join(codegen::SYNTAX_KINDS); | 25 | let syntax_kinds_file = project_root().join(codegen::SYNTAX_KINDS); |
19 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; | 26 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; |
20 | update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; | 27 | update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; |
21 | 28 | ||
22 | let ast_tokens_file = project_root().join(codegen::AST_TOKENS); | 29 | let ast_tokens_file = project_root().join(codegen::AST_TOKENS); |
23 | let contents = generate_tokens(AST_SRC)?; | 30 | let contents = generate_tokens(&ast)?; |
24 | update(ast_tokens_file.as_path(), &contents, mode)?; | 31 | update(ast_tokens_file.as_path(), &contents, mode)?; |
25 | 32 | ||
26 | let ast_nodes_file = project_root().join(codegen::AST_NODES); | 33 | let ast_nodes_file = project_root().join(codegen::AST_NODES); |
27 | let contents = generate_nodes(KINDS_SRC, AST_SRC)?; | 34 | let contents = generate_nodes(KINDS_SRC, &ast)?; |
28 | update(ast_nodes_file.as_path(), &contents, mode)?; | 35 | update(ast_nodes_file.as_path(), &contents, mode)?; |
29 | 36 | ||
30 | Ok(()) | 37 | Ok(()) |
31 | } | 38 | } |
32 | 39 | ||
33 | fn generate_tokens(grammar: AstSrc<'_>) -> Result<String> { | 40 | fn generate_tokens(grammar: &AstSrc) -> Result<String> { |
34 | let tokens = grammar.tokens.iter().map(|token| { | 41 | let tokens = grammar.tokens.iter().map(|token| { |
35 | let name = format_ident!("{}", token); | 42 | let name = format_ident!("{}", token); |
36 | let kind = format_ident!("{}", to_upper_snake_case(token)); | 43 | let kind = format_ident!("{}", to_upper_snake_case(token)); |
@@ -62,13 +69,13 @@ fn generate_tokens(grammar: AstSrc<'_>) -> Result<String> { | |||
62 | Ok(pretty) | 69 | Ok(pretty) |
63 | } | 70 | } |
64 | 71 | ||
65 | fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | 72 | fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> Result<String> { |
66 | let (node_defs, node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar | 73 | let (node_defs, node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar |
67 | .nodes | 74 | .nodes |
68 | .iter() | 75 | .iter() |
69 | .map(|node| { | 76 | .map(|node| { |
70 | let name = format_ident!("{}", node.name); | 77 | let name = format_ident!("{}", node.name); |
71 | let kind = format_ident!("{}", to_upper_snake_case(node.name)); | 78 | let kind = format_ident!("{}", to_upper_snake_case(&node.name)); |
72 | let traits = node.traits.iter().map(|trait_name| { | 79 | let traits = node.traits.iter().map(|trait_name| { |
73 | let trait_name = format_ident!("{}", trait_name); | 80 | let trait_name = format_ident!("{}", trait_name); |
74 | quote!(impl ast::#trait_name for #name {}) | 81 | quote!(impl ast::#trait_name for #name {}) |
@@ -144,25 +151,10 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
144 | quote!(impl ast::#trait_name for #name {}) | 151 | quote!(impl ast::#trait_name for #name {}) |
145 | }); | 152 | }); |
146 | 153 | ||
147 | ( | 154 | let ast_node = if en.name == "Stmt" { |
148 | quote! { | 155 | quote! {} |
149 | #[pretty_doc_comment_placeholder_workaround] | 156 | } else { |
150 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
151 | pub enum #name { | ||
152 | #(#variants(#variants),)* | ||
153 | } | ||
154 | |||
155 | #(#traits)* | ||
156 | }, | ||
157 | quote! { | 157 | quote! { |
158 | #( | ||
159 | impl From<#variants> for #name { | ||
160 | fn from(node: #variants) -> #name { | ||
161 | #name::#variants(node) | ||
162 | } | ||
163 | } | ||
164 | )* | ||
165 | |||
166 | impl AstNode for #name { | 158 | impl AstNode for #name { |
167 | fn can_cast(kind: SyntaxKind) -> bool { | 159 | fn can_cast(kind: SyntaxKind) -> bool { |
168 | match kind { | 160 | match kind { |
@@ -187,13 +179,35 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
187 | } | 179 | } |
188 | } | 180 | } |
189 | } | 181 | } |
182 | } | ||
183 | }; | ||
184 | |||
185 | ( | ||
186 | quote! { | ||
187 | #[pretty_doc_comment_placeholder_workaround] | ||
188 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
189 | pub enum #name { | ||
190 | #(#variants(#variants),)* | ||
191 | } | ||
192 | |||
193 | #(#traits)* | ||
194 | }, | ||
195 | quote! { | ||
196 | #( | ||
197 | impl From<#variants> for #name { | ||
198 | fn from(node: #variants) -> #name { | ||
199 | #name::#variants(node) | ||
200 | } | ||
201 | } | ||
202 | )* | ||
203 | #ast_node | ||
190 | }, | 204 | }, |
191 | ) | 205 | ) |
192 | }) | 206 | }) |
193 | .unzip(); | 207 | .unzip(); |
194 | 208 | ||
195 | let enum_names = grammar.enums.iter().map(|it| it.name); | 209 | let enum_names = grammar.enums.iter().map(|it| &it.name); |
196 | let node_names = grammar.nodes.iter().map(|it| it.name); | 210 | let node_names = grammar.nodes.iter().map(|it| &it.name); |
197 | 211 | ||
198 | let display_impls = | 212 | let display_impls = |
199 | enum_names.chain(node_names.clone()).map(|it| format_ident!("{}", it)).map(|name| { | 213 | enum_names.chain(node_names.clone()).map(|it| format_ident!("{}", it)).map(|name| { |
@@ -212,9 +226,11 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
212 | .nodes | 226 | .nodes |
213 | .iter() | 227 | .iter() |
214 | .map(|kind| to_pascal_case(kind)) | 228 | .map(|kind| to_pascal_case(kind)) |
215 | .filter(|name| !defined_nodes.contains(name.as_str())) | 229 | .filter(|name| !defined_nodes.iter().any(|&it| it == name)) |
216 | { | 230 | { |
217 | eprintln!("Warning: node {} not defined in ast source", node); | 231 | drop(node) |
232 | // TODO: restore this | ||
233 | // eprintln!("Warning: node {} not defined in ast source", node); | ||
218 | } | 234 | } |
219 | 235 | ||
220 | let ast = quote! { | 236 | let ast = quote! { |
@@ -236,12 +252,12 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
236 | let mut res = String::with_capacity(ast.len() * 2); | 252 | let mut res = String::with_capacity(ast.len() * 2); |
237 | 253 | ||
238 | let mut docs = | 254 | let mut docs = |
239 | grammar.nodes.iter().map(|it| it.doc).chain(grammar.enums.iter().map(|it| it.doc)); | 255 | grammar.nodes.iter().map(|it| &it.doc).chain(grammar.enums.iter().map(|it| &it.doc)); |
240 | 256 | ||
241 | for chunk in ast.split("# [ pretty_doc_comment_placeholder_workaround ]") { | 257 | for chunk in ast.split("# [ pretty_doc_comment_placeholder_workaround ]") { |
242 | res.push_str(chunk); | 258 | res.push_str(chunk); |
243 | if let Some(doc) = docs.next() { | 259 | if let Some(doc) = docs.next() { |
244 | write_doc_comment(doc, &mut res); | 260 | write_doc_comment(&doc, &mut res); |
245 | } | 261 | } |
246 | } | 262 | } |
247 | 263 | ||
@@ -249,7 +265,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> { | |||
249 | Ok(pretty) | 265 | Ok(pretty) |
250 | } | 266 | } |
251 | 267 | ||
252 | fn write_doc_comment(contents: &[&str], dest: &mut String) { | 268 | fn write_doc_comment(contents: &[String], dest: &mut String) { |
253 | for line in contents { | 269 | for line in contents { |
254 | writeln!(dest, "///{}", line).unwrap(); | 270 | writeln!(dest, "///{}", line).unwrap(); |
255 | } | 271 | } |
@@ -296,7 +312,7 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> Result<String> { | |||
296 | 312 | ||
297 | let ast = quote! { | 313 | let ast = quote! { |
298 | #![allow(bad_style, missing_docs, unreachable_pub)] | 314 | #![allow(bad_style, missing_docs, unreachable_pub)] |
299 | /// The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT_DEF`. | 315 | /// The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT`. |
300 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | 316 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |
301 | #[repr(u16)] | 317 | #[repr(u16)] |
302 | pub enum SyntaxKind { | 318 | pub enum SyntaxKind { |
@@ -363,6 +379,7 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> Result<String> { | |||
363 | #([#all_keywords_idents] => { $crate::SyntaxKind::#all_keywords };)* | 379 | #([#all_keywords_idents] => { $crate::SyntaxKind::#all_keywords };)* |
364 | [lifetime] => { $crate::SyntaxKind::LIFETIME }; | 380 | [lifetime] => { $crate::SyntaxKind::LIFETIME }; |
365 | [ident] => { $crate::SyntaxKind::IDENT }; | 381 | [ident] => { $crate::SyntaxKind::IDENT }; |
382 | [shebang] => { $crate::SyntaxKind::SHEBANG }; | ||
366 | } | 383 | } |
367 | }; | 384 | }; |
368 | 385 | ||
@@ -413,9 +430,13 @@ fn to_pascal_case(s: &str) -> String { | |||
413 | buf | 430 | buf |
414 | } | 431 | } |
415 | 432 | ||
416 | impl Field<'_> { | 433 | fn pluralize(s: &str) -> String { |
434 | format!("{}s", s) | ||
435 | } | ||
436 | |||
437 | impl Field { | ||
417 | fn is_many(&self) -> bool { | 438 | fn is_many(&self) -> bool { |
418 | matches!(self, Field::Node { src: FieldSrc::Many(_), .. }) | 439 | matches!(self, Field::Node { cardinality: Cardinality::Many, .. }) |
419 | } | 440 | } |
420 | fn token_kind(&self) -> Option<proc_macro2::TokenStream> { | 441 | fn token_kind(&self) -> Option<proc_macro2::TokenStream> { |
421 | match self { | 442 | match self { |
@@ -429,7 +450,7 @@ impl Field<'_> { | |||
429 | fn method_name(&self) -> proc_macro2::Ident { | 450 | fn method_name(&self) -> proc_macro2::Ident { |
430 | match self { | 451 | match self { |
431 | Field::Token(name) => { | 452 | Field::Token(name) => { |
432 | let name = match *name { | 453 | let name = match name.as_str() { |
433 | ";" => "semicolon", | 454 | ";" => "semicolon", |
434 | "->" => "thin_arrow", | 455 | "->" => "thin_arrow", |
435 | "'{'" => "l_curly", | 456 | "'{'" => "l_curly", |
@@ -448,29 +469,273 @@ impl Field<'_> { | |||
448 | "." => "dot", | 469 | "." => "dot", |
449 | ".." => "dotdot", | 470 | ".." => "dotdot", |
450 | "..." => "dotdotdot", | 471 | "..." => "dotdotdot", |
472 | "..=" => "dotdoteq", | ||
451 | "=>" => "fat_arrow", | 473 | "=>" => "fat_arrow", |
452 | "@" => "at", | 474 | "@" => "at", |
453 | ":" => "colon", | 475 | ":" => "colon", |
454 | "::" => "coloncolon", | 476 | "::" => "coloncolon", |
455 | "#" => "pound", | 477 | "#" => "pound", |
456 | "?" => "question_mark", | 478 | "?" => "question_mark", |
479 | "," => "comma", | ||
457 | _ => name, | 480 | _ => name, |
458 | }; | 481 | }; |
459 | format_ident!("{}_token", name) | 482 | format_ident!("{}_token", name) |
460 | } | 483 | } |
461 | Field::Node { name, src } => match src { | 484 | Field::Node { name, .. } => { |
462 | FieldSrc::Shorthand => format_ident!("{}", to_lower_snake_case(name)), | 485 | if name == "type" { |
463 | _ => format_ident!("{}", name), | 486 | format_ident!("ty") |
464 | }, | 487 | } else { |
488 | format_ident!("{}", name) | ||
489 | } | ||
490 | } | ||
465 | } | 491 | } |
466 | } | 492 | } |
467 | fn ty(&self) -> proc_macro2::Ident { | 493 | fn ty(&self) -> proc_macro2::Ident { |
468 | match self { | 494 | match self { |
469 | Field::Token(_) => format_ident!("SyntaxToken"), | 495 | Field::Token(_) => format_ident!("SyntaxToken"), |
470 | Field::Node { name, src } => match src { | 496 | Field::Node { ty, .. } => format_ident!("{}", ty), |
471 | FieldSrc::Optional(ty) | FieldSrc::Many(ty) => format_ident!("{}", ty), | ||
472 | FieldSrc::Shorthand => format_ident!("{}", name), | ||
473 | }, | ||
474 | } | 497 | } |
475 | } | 498 | } |
476 | } | 499 | } |
500 | |||
501 | fn lower(grammar: &Grammar) -> AstSrc { | ||
502 | let mut res = AstSrc::default(); | ||
503 | res.tokens = vec!["Whitespace".into(), "Comment".into(), "String".into(), "RawString".into()]; | ||
504 | |||
505 | let nodes = grammar.iter().collect::<Vec<_>>(); | ||
506 | |||
507 | for &node in &nodes { | ||
508 | let name = grammar[node].name.clone(); | ||
509 | let rule = &grammar[node].rule; | ||
510 | match lower_enum(grammar, rule) { | ||
511 | Some(variants) => { | ||
512 | let enum_src = AstEnumSrc { doc: Vec::new(), name, traits: Vec::new(), variants }; | ||
513 | res.enums.push(enum_src); | ||
514 | } | ||
515 | None => { | ||
516 | let mut fields = Vec::new(); | ||
517 | lower_rule(&mut fields, grammar, None, rule); | ||
518 | res.nodes.push(AstNodeSrc { doc: Vec::new(), name, traits: Vec::new(), fields }); | ||
519 | } | ||
520 | } | ||
521 | } | ||
522 | |||
523 | deduplicate_fields(&mut res); | ||
524 | extract_enums(&mut res); | ||
525 | extract_struct_traits(&mut res); | ||
526 | extract_enum_traits(&mut res); | ||
527 | res | ||
528 | } | ||
529 | |||
530 | fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> { | ||
531 | let alternatives = match rule { | ||
532 | Rule::Alt(it) => it, | ||
533 | _ => return None, | ||
534 | }; | ||
535 | let mut variants = Vec::new(); | ||
536 | for alternative in alternatives { | ||
537 | match alternative { | ||
538 | Rule::Node(it) => variants.push(grammar[*it].name.clone()), | ||
539 | Rule::Token(it) if grammar[*it].name == ";" => (), | ||
540 | _ => return None, | ||
541 | } | ||
542 | } | ||
543 | Some(variants) | ||
544 | } | ||
545 | |||
546 | fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, rule: &Rule) { | ||
547 | if lower_comma_list(acc, grammar, label, rule) { | ||
548 | return; | ||
549 | } | ||
550 | |||
551 | match rule { | ||
552 | Rule::Node(node) => { | ||
553 | let ty = grammar[*node].name.clone(); | ||
554 | let name = label.cloned().unwrap_or_else(|| to_lower_snake_case(&ty)); | ||
555 | let field = Field::Node { name, ty, cardinality: Cardinality::Optional }; | ||
556 | acc.push(field); | ||
557 | } | ||
558 | Rule::Token(token) => { | ||
559 | assert!(label.is_none()); | ||
560 | let mut name = grammar[*token].name.clone(); | ||
561 | if name != "int_number" && name != "string" { | ||
562 | if "[]{}()".contains(&name) { | ||
563 | name = format!("'{}'", name); | ||
564 | } | ||
565 | let field = Field::Token(name); | ||
566 | acc.push(field); | ||
567 | } | ||
568 | } | ||
569 | Rule::Rep(inner) => { | ||
570 | if let Rule::Node(node) = &**inner { | ||
571 | let ty = grammar[*node].name.clone(); | ||
572 | let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty))); | ||
573 | let field = Field::Node { name, ty, cardinality: Cardinality::Many }; | ||
574 | acc.push(field); | ||
575 | return; | ||
576 | } | ||
577 | todo!("{:?}", rule) | ||
578 | } | ||
579 | Rule::Labeled { label: l, rule } => { | ||
580 | assert!(label.is_none()); | ||
581 | let manually_implemented = matches!( | ||
582 | l.as_str(), | ||
583 | "lhs" | ||
584 | | "rhs" | ||
585 | | "then_branch" | ||
586 | | "else_branch" | ||
587 | | "start" | ||
588 | | "end" | ||
589 | | "op" | ||
590 | | "index" | ||
591 | | "base" | ||
592 | | "value" | ||
593 | | "trait" | ||
594 | | "self_ty" | ||
595 | ); | ||
596 | if manually_implemented { | ||
597 | return; | ||
598 | } | ||
599 | lower_rule(acc, grammar, Some(l), rule); | ||
600 | } | ||
601 | Rule::Seq(rules) | Rule::Alt(rules) => { | ||
602 | for rule in rules { | ||
603 | lower_rule(acc, grammar, label, rule) | ||
604 | } | ||
605 | } | ||
606 | Rule::Opt(rule) => lower_rule(acc, grammar, label, rule), | ||
607 | } | ||
608 | } | ||
609 | |||
610 | // (T (',' T)* ','?) | ||
611 | fn lower_comma_list( | ||
612 | acc: &mut Vec<Field>, | ||
613 | grammar: &Grammar, | ||
614 | label: Option<&String>, | ||
615 | rule: &Rule, | ||
616 | ) -> bool { | ||
617 | let rule = match rule { | ||
618 | Rule::Seq(it) => it, | ||
619 | _ => return false, | ||
620 | }; | ||
621 | let (node, repeat, trailing_comma) = match rule.as_slice() { | ||
622 | [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => { | ||
623 | (node, repeat, trailing_comma) | ||
624 | } | ||
625 | _ => return false, | ||
626 | }; | ||
627 | let repeat = match &**repeat { | ||
628 | Rule::Seq(it) => it, | ||
629 | _ => return false, | ||
630 | }; | ||
631 | match repeat.as_slice() { | ||
632 | [comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (), | ||
633 | _ => return false, | ||
634 | } | ||
635 | let ty = grammar[*node].name.clone(); | ||
636 | let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty))); | ||
637 | let field = Field::Node { name, ty, cardinality: Cardinality::Many }; | ||
638 | acc.push(field); | ||
639 | true | ||
640 | } | ||
641 | |||
642 | fn deduplicate_fields(ast: &mut AstSrc) { | ||
643 | for node in &mut ast.nodes { | ||
644 | let mut i = 0; | ||
645 | 'outer: while i < node.fields.len() { | ||
646 | for j in 0..i { | ||
647 | let f1 = &node.fields[i]; | ||
648 | let f2 = &node.fields[j]; | ||
649 | if f1 == f2 { | ||
650 | node.fields.remove(i); | ||
651 | continue 'outer; | ||
652 | } | ||
653 | } | ||
654 | i += 1; | ||
655 | } | ||
656 | } | ||
657 | } | ||
658 | |||
659 | fn extract_enums(ast: &mut AstSrc) { | ||
660 | for node in &mut ast.nodes { | ||
661 | for enm in &ast.enums { | ||
662 | let mut to_remove = Vec::new(); | ||
663 | for (i, field) in node.fields.iter().enumerate() { | ||
664 | let ty = field.ty().to_string(); | ||
665 | if enm.variants.iter().any(|it| it == &ty) { | ||
666 | to_remove.push(i); | ||
667 | } | ||
668 | } | ||
669 | if to_remove.len() == enm.variants.len() { | ||
670 | node.remove_field(to_remove); | ||
671 | let ty = enm.name.clone(); | ||
672 | let name = to_lower_snake_case(&ty); | ||
673 | node.fields.push(Field::Node { name, ty, cardinality: Cardinality::Optional }); | ||
674 | } | ||
675 | } | ||
676 | } | ||
677 | } | ||
678 | |||
679 | fn extract_struct_traits(ast: &mut AstSrc) { | ||
680 | let traits: &[(&str, &[&str])] = &[ | ||
681 | ("AttrsOwner", &["attrs"]), | ||
682 | ("NameOwner", &["name"]), | ||
683 | ("VisibilityOwner", &["visibility"]), | ||
684 | ("GenericParamsOwner", &["generic_param_list", "where_clause"]), | ||
685 | ("TypeBoundsOwner", &["type_bound_list", "colon_token"]), | ||
686 | ("ModuleItemOwner", &["items"]), | ||
687 | ("LoopBodyOwner", &["label", "loop_body"]), | ||
688 | ("ArgListOwner", &["arg_list"]), | ||
689 | ]; | ||
690 | |||
691 | for node in &mut ast.nodes { | ||
692 | for (name, methods) in traits { | ||
693 | extract_struct_trait(node, name, methods); | ||
694 | } | ||
695 | } | ||
696 | } | ||
697 | |||
698 | fn extract_struct_trait(node: &mut AstNodeSrc, trait_name: &str, methods: &[&str]) { | ||
699 | let mut to_remove = Vec::new(); | ||
700 | for (i, field) in node.fields.iter().enumerate() { | ||
701 | let method_name = field.method_name().to_string(); | ||
702 | if methods.iter().any(|&it| it == &method_name) { | ||
703 | to_remove.push(i); | ||
704 | } | ||
705 | } | ||
706 | if to_remove.len() == methods.len() { | ||
707 | node.traits.push(trait_name.to_string()); | ||
708 | node.remove_field(to_remove); | ||
709 | } | ||
710 | } | ||
711 | |||
712 | fn extract_enum_traits(ast: &mut AstSrc) { | ||
713 | for enm in &mut ast.enums { | ||
714 | if enm.name == "Stmt" { | ||
715 | continue; | ||
716 | } | ||
717 | let nodes = &ast.nodes; | ||
718 | let mut variant_traits = enm | ||
719 | .variants | ||
720 | .iter() | ||
721 | .map(|var| nodes.iter().find(|it| &it.name == var).unwrap()) | ||
722 | .map(|node| node.traits.iter().cloned().collect::<BTreeSet<_>>()); | ||
723 | |||
724 | let mut enum_traits = match variant_traits.next() { | ||
725 | Some(it) => it, | ||
726 | None => continue, | ||
727 | }; | ||
728 | for traits in variant_traits { | ||
729 | enum_traits = enum_traits.intersection(&traits).cloned().collect(); | ||
730 | } | ||
731 | enm.traits = enum_traits.into_iter().collect(); | ||
732 | } | ||
733 | } | ||
734 | |||
735 | impl AstNodeSrc { | ||
736 | fn remove_field(&mut self, to_remove: Vec<usize>) { | ||
737 | to_remove.into_iter().rev().for_each(|idx| { | ||
738 | self.fields.remove(idx); | ||
739 | }); | ||
740 | } | ||
741 | } | ||
diff --git a/xtask/src/codegen/gen_unstable_future_descriptor.rs b/xtask/src/codegen/gen_unstable_future_descriptor.rs index 09c4f61f2..298696fbb 100644 --- a/xtask/src/codegen/gen_unstable_future_descriptor.rs +++ b/xtask/src/codegen/gen_unstable_future_descriptor.rs | |||
@@ -42,7 +42,7 @@ pub fn generate_unstable_future_descriptor(mode: Mode) -> Result<()> { | |||
42 | let ts = quote! { | 42 | let ts = quote! { |
43 | use crate::completion::LintCompletion; | 43 | use crate::completion::LintCompletion; |
44 | 44 | ||
45 | const UNSTABLE_FEATURE_DESCRIPTOR: &[LintCompletion] = &[ | 45 | pub const UNSTABLE_FEATURE_DESCRIPTOR: &[LintCompletion] = &[ |
46 | #(#definitions),* | 46 | #(#definitions),* |
47 | ]; | 47 | ]; |
48 | }; | 48 | }; |
diff --git a/xtask/src/codegen/rust.ungram b/xtask/src/codegen/rust.ungram new file mode 100644 index 000000000..aca23890c --- /dev/null +++ b/xtask/src/codegen/rust.ungram | |||
@@ -0,0 +1,587 @@ | |||
1 | //*************************// | ||
2 | // Names, Paths and Macros // | ||
3 | //*************************// | ||
4 | |||
5 | Name = | ||
6 | 'ident' | ||
7 | |||
8 | NameRef = | ||
9 | 'ident' | 'int_number' | ||
10 | |||
11 | Path = | ||
12 | (qualifier:Path '::')? segment:PathSegment | ||
13 | |||
14 | PathSegment = | ||
15 | 'crate' | 'self' | 'super' | ||
16 | | '::' NameRef | ||
17 | | NameRef GenericArgList? | ||
18 | | NameRef ParamList RetType? | ||
19 | | '<' PathType ('as' PathType)? '>' | ||
20 | |||
21 | GenericArgList = | ||
22 | '::'? '<' (GenericArg (',' GenericArg)* ','?)? '>' | ||
23 | |||
24 | GenericArg = | ||
25 | TypeArg | ||
26 | | AssocTypeArg | ||
27 | | LifetimeArg | ||
28 | | ConstArg | ||
29 | |||
30 | TypeArg = | ||
31 | Type | ||
32 | |||
33 | AssocTypeArg = | ||
34 | NameRef (':' TypeBoundList | '=' Type) | ||
35 | |||
36 | LifetimeArg = | ||
37 | 'lifetime' | ||
38 | |||
39 | ConstArg = | ||
40 | Expr | ||
41 | |||
42 | MacroCall = | ||
43 | Attr* Path '!' Name? TokenTree ';'? | ||
44 | |||
45 | TokenTree = | ||
46 | '(' ')' | ||
47 | | '{' '}' | ||
48 | | '[' ']' | ||
49 | |||
50 | MacroItems = | ||
51 | Item* | ||
52 | |||
53 | MacroStmts = | ||
54 | statements:Stmt* | ||
55 | Expr? | ||
56 | |||
57 | //*************************// | ||
58 | // Items // | ||
59 | //*************************// | ||
60 | |||
61 | SourceFile = | ||
62 | 'shebang'? | ||
63 | Attr* | ||
64 | Item* | ||
65 | |||
66 | Item = | ||
67 | Const | ||
68 | | Enum | ||
69 | | ExternBlock | ||
70 | | ExternCrate | ||
71 | | Fn | ||
72 | | Impl | ||
73 | | MacroCall | ||
74 | | Module | ||
75 | | Static | ||
76 | | Struct | ||
77 | | Trait | ||
78 | | TypeAlias | ||
79 | | Union | ||
80 | | Use | ||
81 | |||
82 | Module = | ||
83 | Attr* Visibility? 'mod' Name | ||
84 | (ItemList | ';') | ||
85 | |||
86 | ItemList = | ||
87 | '{' Attr* Item* '}' | ||
88 | |||
89 | ExternCrate = | ||
90 | Attr* Visibility? 'extern' 'crate' (NameRef | 'self') Rename? ';' | ||
91 | |||
92 | Rename = | ||
93 | 'as' (Name | '_') | ||
94 | |||
95 | Use = | ||
96 | Attr* Visibility? 'use' UseTree ';' | ||
97 | |||
98 | UseTree = | ||
99 | (Path? '::')? ('*' | UseTreeList ) | ||
100 | | Path Rename? | ||
101 | |||
102 | UseTreeList = | ||
103 | '{' (UseTree (',' UseTree)* ','?)? '}' | ||
104 | |||
105 | Fn = | ||
106 | Attr* Visibility? | ||
107 | 'default'? ('async' | 'const')? 'unsafe'? Abi? | ||
108 | 'fn' Name GenericParamList? ParamList RetType? | ||
109 | WhereClause? | ||
110 | (body:BlockExpr | ';') | ||
111 | |||
112 | Abi = | ||
113 | 'extern' 'string'? | ||
114 | |||
115 | ParamList = | ||
116 | '('( | ||
117 | SelfParam | ||
118 | | (SelfParam ',')? (Param (',' Param)* ','?)? | ||
119 | )')' | ||
120 | |||
121 | SelfParam = | ||
122 | Attr* ( | ||
123 | ('&' 'lifetime'?)? 'mut'? 'self' | ||
124 | | 'mut'? 'self' ':' Type | ||
125 | ) | ||
126 | |||
127 | Param = | ||
128 | Attr* ( | ||
129 | Pat (':' Type) | ||
130 | | Type | ||
131 | | '...' | ||
132 | ) | ||
133 | |||
134 | RetType = | ||
135 | '->' Type | ||
136 | |||
137 | TypeAlias = | ||
138 | Attr* Visibility? 'default'? 'type' Name GenericParamList? (':' TypeBoundList?)? WhereClause? | ||
139 | '=' Type ';' | ||
140 | |||
141 | Struct = | ||
142 | Attr* Visibility? 'struct' Name GenericParamList? ( | ||
143 | WhereClause? (RecordFieldList | ';') | ||
144 | | TupleFieldList WhereClause? ';' | ||
145 | ) | ||
146 | |||
147 | RecordFieldList = | ||
148 | '{' fields:(RecordField (',' RecordField)* ','?)? '}' | ||
149 | |||
150 | RecordField = | ||
151 | Attr* Visibility? Name ':' Type | ||
152 | |||
153 | TupleFieldList = | ||
154 | '(' fields:(TupleField (',' TupleField)* ','?)? ')' | ||
155 | |||
156 | TupleField = | ||
157 | Attr* Visibility? Type | ||
158 | |||
159 | FieldList = | ||
160 | RecordFieldList | ||
161 | | TupleFieldList | ||
162 | |||
163 | Enum = | ||
164 | Attr* Visibility? 'enum' Name GenericParamList? WhereClause? | ||
165 | VariantList | ||
166 | |||
167 | VariantList = | ||
168 | '{' (Variant (',' Variant)* ','?)? '}' | ||
169 | |||
170 | Variant = | ||
171 | Attr* Visibility? Name FieldList ('=' Expr)? | ||
172 | |||
173 | Union = | ||
174 | Attr* Visibility? 'union' Name GenericParamList? WhereClause? | ||
175 | RecordFieldList | ||
176 | |||
177 | AdtDef = | ||
178 | Enum | ||
179 | | Struct | ||
180 | | Union | ||
181 | |||
182 | Const = | ||
183 | Attr* Visibility? 'default'? 'const' (Name | '_') ':' Type | ||
184 | '=' body:Expr ';' | ||
185 | |||
186 | Static = | ||
187 | Attr* Visibility? 'static'? 'mut'? Name ':' Type | ||
188 | '=' body:Expr ';' | ||
189 | |||
190 | Trait = | ||
191 | Attr* Visibility? 'unsafe'? 'auto'? 'trait' Name GenericParamList | ||
192 | (':' TypeBoundList?)? WhereClause | ||
193 | AssocItemList | ||
194 | |||
195 | AssocItemList = | ||
196 | '{' Attr* AssocItem* '}' | ||
197 | |||
198 | AssocItem = | ||
199 | Const | ||
200 | | Fn | ||
201 | | MacroCall | ||
202 | | TypeAlias | ||
203 | |||
204 | Impl = | ||
205 | Attr* Visibility? | ||
206 | 'default'? 'unsafe'? 'impl' 'const'? GenericParamList? | ||
207 | ('!'? target_trait:Type 'for')? target_type:Type | ||
208 | WhereClause? | ||
209 | AssocItemList | ||
210 | |||
211 | ExternBlock = | ||
212 | Attr* Abi ExternItemList | ||
213 | |||
214 | ExternItemList = | ||
215 | '{' Attr* ExternItem* '}' | ||
216 | |||
217 | ExternItem = | ||
218 | Fn | Static | MacroCall | ||
219 | |||
220 | GenericParamList = | ||
221 | '<' (GenericParam (',' GenericParam)* ','?)? '>' | ||
222 | |||
223 | GenericParam = | ||
224 | ConstParam | ||
225 | | LifetimeParam | ||
226 | | TypeParam | ||
227 | |||
228 | TypeParam = | ||
229 | Attr* Name (':' TypeBoundList?)? | ||
230 | ('=' default_type:Type)? | ||
231 | |||
232 | ConstParam = | ||
233 | Attr* 'const' Name ':' Type | ||
234 | ('=' default_val:Expr)? | ||
235 | |||
236 | LifetimeParam = | ||
237 | Attr* 'lifetime' (':' TypeBoundList?)? | ||
238 | |||
239 | WhereClause = | ||
240 | 'where' predicates:(WherePred (',' WherePred)* ','?) | ||
241 | |||
242 | WherePred = | ||
243 | ('for' GenericParamList)? ('lifetime' | Type) ':' TypeBoundList | ||
244 | |||
245 | Visibility = | ||
246 | 'pub' ('(' | ||
247 | 'super' | ||
248 | | 'self' | ||
249 | | 'crate' | ||
250 | | 'in' Path | ||
251 | ')')? | ||
252 | |||
253 | Attr = | ||
254 | '#' '!'? '[' Path ('=' Literal | TokenTree)? ']' | ||
255 | |||
256 | //****************************// | ||
257 | // Statements and Expressions // | ||
258 | //****************************// | ||
259 | |||
260 | Stmt = | ||
261 | ExprStmt | ||
262 | | Item | ||
263 | | LetStmt | ||
264 | |||
265 | LetStmt = | ||
266 | Attr* 'let' Pat (':' Type)? | ||
267 | '=' initializer:Expr ';' | ||
268 | |||
269 | ExprStmt = | ||
270 | Attr* Expr ';'? | ||
271 | |||
272 | Expr = | ||
273 | ArrayExpr | ||
274 | | AwaitExpr | ||
275 | | BinExpr | ||
276 | | BlockExpr | ||
277 | | BoxExpr | ||
278 | | BreakExpr | ||
279 | | CallExpr | ||
280 | | CastExpr | ||
281 | | ClosureExpr | ||
282 | | ContinueExpr | ||
283 | | EffectExpr | ||
284 | | FieldExpr | ||
285 | | ForExpr | ||
286 | | IfExpr | ||
287 | | IndexExpr | ||
288 | | Literal | ||
289 | | LoopExpr | ||
290 | | MacroCall | ||
291 | | MatchExpr | ||
292 | | MethodCallExpr | ||
293 | | ParenExpr | ||
294 | | PathExpr | ||
295 | | PrefixExpr | ||
296 | | RangeExpr | ||
297 | | RecordExpr | ||
298 | | RefExpr | ||
299 | | ReturnExpr | ||
300 | | TryExpr | ||
301 | | TupleExpr | ||
302 | | WhileExpr | ||
303 | |||
304 | Literal = | ||
305 | Attr* value:( | ||
306 | 'int_number' | 'float_number' | ||
307 | | 'string' | 'raw_string' | ||
308 | | 'byte_string' | 'raw_byte_string' | ||
309 | | 'true' | 'false' | ||
310 | | 'char' | 'byte' | ||
311 | ) | ||
312 | |||
313 | PathExpr = | ||
314 | Attr* Path | ||
315 | |||
316 | BlockExpr = | ||
317 | '{' | ||
318 | Attr* | ||
319 | statements:Stmt* | ||
320 | Expr? | ||
321 | '}' | ||
322 | |||
323 | RefExpr = | ||
324 | Attr* '&' ('raw' |'mut' | 'const') Expr | ||
325 | |||
326 | TryExpr = | ||
327 | Attr* Expr '?' | ||
328 | |||
329 | EffectExpr = | ||
330 | Attr* Label? ('try' | 'unsafe' | 'async') BlockExpr | ||
331 | |||
332 | PrefixExpr = | ||
333 | Attr* op:('-' | '!' | '*') Expr | ||
334 | |||
335 | BinExpr = | ||
336 | Attr* | ||
337 | lhs:Expr | ||
338 | op:( | ||
339 | '||' | '&&' | ||
340 | | '==' | '!=' | '<=' | '>=' | '<' | '>' | ||
341 | | '+' | '*' | '-' | '/' | '%' | '<<' | '>>' | '^' | '|' | '&' | ||
342 | | '=' | '+=' | '/=' | '*=' | '%=' | '>>=' | '<<=' | '-=' | '|=' | '&=' | '^=' | ||
343 | ) | ||
344 | rhs:Expr | ||
345 | |||
346 | CastExpr = | ||
347 | Attr* Expr 'as' Type | ||
348 | |||
349 | ParenExpr = | ||
350 | Attr* '(' Attr* Expr ')' | ||
351 | |||
352 | ArrayExpr = | ||
353 | Attr* '[' Attr* ( | ||
354 | (Expr (',' Expr)* ','?)? | ||
355 | | Expr ';' Expr | ||
356 | ) ']' | ||
357 | |||
358 | IndexExpr = | ||
359 | Attr* base:Expr '[' index:Expr ']' | ||
360 | |||
361 | TupleExpr = | ||
362 | Attr* '(' Attr* fields:(Expr (',' Expr)* ','?)? ')' | ||
363 | |||
364 | RecordExpr = | ||
365 | Path RecordExprFieldList | ||
366 | |||
367 | RecordExprFieldList = | ||
368 | '{' | ||
369 | Attr* | ||
370 | fields:(RecordExprField (',' RecordExprField)* ','?) | ||
371 | ('..' spread:Expr)? | ||
372 | '}' | ||
373 | |||
374 | RecordExprField = | ||
375 | Attr* NameRef (':' Expr)? | ||
376 | |||
377 | CallExpr = | ||
378 | Attr* Expr ArgList | ||
379 | |||
380 | ArgList = | ||
381 | '(' args:(Expr (',' Expr)* ','?)? ')' | ||
382 | |||
383 | MethodCallExpr = | ||
384 | Attr* Expr '.' NameRef GenericArgList? ArgList | ||
385 | |||
386 | FieldExpr = | ||
387 | Attr* Expr '.' NameRef | ||
388 | |||
389 | ClosureExpr = | ||
390 | Attr* 'static'? 'async'? 'move'? ParamList RetType? | ||
391 | body:Expr | ||
392 | |||
393 | IfExpr = | ||
394 | Attr* 'if' Condition then_branch:BlockExpr | ||
395 | ('else' else_branch:(IfExpr | BlockExpr))? | ||
396 | |||
397 | Condition = | ||
398 | 'let' Pat '=' Expr | ||
399 | | Expr | ||
400 | |||
401 | LoopExpr = | ||
402 | Attr* Label? 'loop' | ||
403 | loop_body:BlockExpr | ||
404 | |||
405 | ForExpr = | ||
406 | Attr* Label? 'for' Pat 'in' iterable:Expr | ||
407 | loop_body:BlockExpr | ||
408 | |||
409 | WhileExpr = | ||
410 | Attr* Label? 'while' Condition | ||
411 | loop_body:BlockExpr | ||
412 | |||
413 | Label = | ||
414 | 'lifetime' | ||
415 | |||
416 | BreakExpr = | ||
417 | Attr* 'break' 'lifetime'? Expr? | ||
418 | |||
419 | ContinueExpr = | ||
420 | Attr* 'continue' 'lifetime'? | ||
421 | |||
422 | RangeExpr = | ||
423 | Attr* start:Expr? op:('..' | '..=') end:Expr? | ||
424 | |||
425 | MatchExpr = | ||
426 | Attr* 'match' Expr MatchArmList | ||
427 | |||
428 | MatchArmList = | ||
429 | '{' | ||
430 | Attr* | ||
431 | arms:MatchArm* | ||
432 | '}' | ||
433 | |||
434 | MatchArm = | ||
435 | Attr* Pat guard:MatchGuard? '=>' Expr ','? | ||
436 | |||
437 | MatchGuard = | ||
438 | 'if' Expr | ||
439 | |||
440 | ReturnExpr = | ||
441 | Attr* 'return' Expr? | ||
442 | |||
443 | AwaitExpr = | ||
444 | Attr* Expr '.' 'await' | ||
445 | |||
446 | BoxExpr = | ||
447 | Attr* 'box' Expr | ||
448 | |||
449 | //*************************// | ||
450 | // Types // | ||
451 | //*************************// | ||
452 | |||
453 | Type = | ||
454 | ArrayType | ||
455 | | DynTraitType | ||
456 | | FnPointerType | ||
457 | | ForType | ||
458 | | ImplTraitType | ||
459 | | InferType | ||
460 | | NeverType | ||
461 | | ParenType | ||
462 | | PathType | ||
463 | | PointerType | ||
464 | | ReferenceType | ||
465 | | SliceType | ||
466 | | TupleType | ||
467 | |||
468 | ParenType = | ||
469 | '(' Type ')' | ||
470 | |||
471 | NeverType = | ||
472 | '!' | ||
473 | |||
474 | PathType = | ||
475 | Path | ||
476 | |||
477 | TupleType = | ||
478 | '(' fields:(Type (',' Type)* ','?)? ')' | ||
479 | |||
480 | PointerType = | ||
481 | '*' ('const' | 'mut') Type | ||
482 | |||
483 | ReferenceType = | ||
484 | '&' 'lifetime'? 'mut'? Type | ||
485 | |||
486 | ArrayType = | ||
487 | '[' Type ';' Expr ']' | ||
488 | |||
489 | SliceType = | ||
490 | '[' Type ']' | ||
491 | |||
492 | InferType = | ||
493 | '_' | ||
494 | |||
495 | FnPointerType = | ||
496 | 'const'? 'async'? 'unsafe'? Abi? 'fn' ParamList RetType? | ||
497 | |||
498 | ForType = | ||
499 | 'for' GenericParamList Type | ||
500 | |||
501 | ImplTraitType = | ||
502 | 'impl' TypeBoundList | ||
503 | |||
504 | DynTraitType = | ||
505 | 'dyn' TypeBoundList | ||
506 | |||
507 | TypeBoundList = | ||
508 | bounds:(TypeBound ('+' TypeBound)* '+'?) | ||
509 | |||
510 | TypeBound = | ||
511 | 'lifetime' | ||
512 | | '?'? Type | ||
513 | |||
514 | //************************// | ||
515 | // Patterns // | ||
516 | //************************// | ||
517 | |||
518 | Pat = | ||
519 | IdentPat | ||
520 | | BoxPat | ||
521 | | RestPat | ||
522 | | LiteralPat | ||
523 | | MacroPat | ||
524 | | OrPat | ||
525 | | ParenPat | ||
526 | | PathPat | ||
527 | | WildcardPat | ||
528 | | RangePat | ||
529 | | RecordPat | ||
530 | | RefPat | ||
531 | | SlicePat | ||
532 | | TuplePat | ||
533 | | TupleStructPat | ||
534 | |||
535 | LiteralPat = | ||
536 | Literal | ||
537 | |||
538 | IdentPat = | ||
539 | Attr* 'ref'? 'mut'? Name ('@' Pat)? | ||
540 | |||
541 | WildcardPat = | ||
542 | '_' | ||
543 | |||
544 | RangePat = | ||
545 | start:Pat op:('..' | '..=') end:Pat | ||
546 | |||
547 | RefPat = | ||
548 | '&' 'mut'? Pat | ||
549 | |||
550 | RecordPat = | ||
551 | Path RecordPatFieldList | ||
552 | |||
553 | RecordPatFieldList = | ||
554 | '{' | ||
555 | fields:(RecordPatField (',' RecordPatField)* ','?) | ||
556 | '..'? | ||
557 | '}' | ||
558 | |||
559 | RecordPatField = | ||
560 | Attr* (NameRef ':')? Pat | ||
561 | |||
562 | TupleStructPat = | ||
563 | Path '(' fields:(Pat (',' Pat)* ','?)? ')' | ||
564 | |||
565 | TuplePat = | ||
566 | '(' fields:(Pat (',' Pat)* ','?)? ')' | ||
567 | |||
568 | ParenPat = | ||
569 | '(' Pat ')' | ||
570 | |||
571 | SlicePat = | ||
572 | '[' (Pat (',' Pat)* ','?)? ']' | ||
573 | |||
574 | PathPat = | ||
575 | Path | ||
576 | |||
577 | OrPat = | ||
578 | (Pat ('|' Pat)* '|'?) | ||
579 | |||
580 | BoxPat = | ||
581 | 'box' Pat | ||
582 | |||
583 | RestPat = | ||
584 | '..' | ||
585 | |||
586 | MacroPat = | ||
587 | MacroCall | ||