feat: add support for feature attributes in struct literal

Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
This commit is contained in:
Benjamin Coenen 2020-04-09 18:37:34 +02:00
commit c1317d6923
28 changed files with 7648 additions and 8155 deletions

View file

@ -1,5 +1,5 @@
use ra_syntax::{
ast::{self, AstNode, NameOwner, TypeParamsOwner},
ast::{self, AstNode, AstToken, NameOwner, TypeParamsOwner},
TextUnit,
};
use stdx::{format_to, SepBy};
@ -42,7 +42,7 @@ pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> {
if let Some(type_params) = type_params {
let lifetime_params = type_params
.lifetime_params()
.filter_map(|it| it.lifetime_token())
.filter_map(|it| it.lifetime())
.map(|it| it.text().clone());
let type_params =
type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());

View file

@ -1,7 +1,8 @@
use hir::Adt;
use ra_syntax::{
ast::{
self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner,
self, AstNode, AstToken, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner,
VisibilityOwner,
},
TextUnit, T,
};
@ -105,7 +106,7 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String {
if let Some(type_params) = type_params {
let lifetime_params = type_params
.lifetime_params()
.filter_map(|it| it.lifetime_token())
.filter_map(|it| it.lifetime())
.map(|it| it.text().clone());
let type_params =
type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());

View file

@ -124,7 +124,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
}
}
if ast::Stmt::cast(node.clone()).is_some() {
if ast::Stmt::cast(node.clone().into()).is_some() {
return Some((node, false));
}

View file

@ -3,7 +3,7 @@ use std::iter::successors;
use ra_syntax::{
algo::{neighbor, SyntaxRewriter},
ast::{self, edit::AstNodeEdit, make},
AstNode, Direction, InsertPosition, SyntaxElement, T,
AstNode, AstToken, Direction, InsertPosition, SyntaxElement, T,
};
use crate::{Assist, AssistCtx, AssistId};
@ -82,7 +82,7 @@ fn try_merge_trees(old: &ast::UseTree, new: &ast::UseTree) -> Option<ast::UseTre
.filter(|it| it.kind() != T!['{'] && it.kind() != T!['}']),
);
let use_tree_list = lhs.use_tree_list()?;
let pos = InsertPosition::Before(use_tree_list.r_curly()?.into());
let pos = InsertPosition::Before(use_tree_list.r_curly()?.syntax().clone().into());
let use_tree_list = use_tree_list.insert_children(pos, to_insert);
Some(lhs.with_use_tree_list(use_tree_list))
}

View file

@ -60,10 +60,10 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
} else {
// Unwrap `{ continue; }`
let (stmt,) = block.statements().next_tuple()?;
if has_anything_else(stmt.syntax()) {
if let ast::Stmt::ExprStmt(expr_stmt) = stmt {
if has_anything_else(expr_stmt.syntax()) {
return None;
}
if let ast::Stmt::ExprStmt(expr_stmt) = stmt {
let expr = expr_stmt.expr()?;
match expr.syntax().kind() {
CONTINUE_EXPR | BREAK_EXPR | RETURN_EXPR => return Some(expr),

View file

@ -497,14 +497,16 @@ impl ExprCollector<'_> {
self.collect_block_items(&block);
let statements = block
.statements()
.map(|s| match s {
.filter_map(|s| match s {
ast::Stmt::LetStmt(stmt) => {
let pat = self.collect_pat_opt(stmt.pat());
let type_ref = stmt.ascribed_type().map(TypeRef::from_ast);
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
Statement::Let { pat, type_ref, initializer }
Some(Statement::Let { pat, type_ref, initializer })
}
ast::Stmt::ExprStmt(stmt) => {
Some(Statement::Expr(self.collect_expr_opt(stmt.expr())))
}
ast::Stmt::ExprStmt(stmt) => Statement::Expr(self.collect_expr_opt(stmt.expr())),
})
.collect();
let tail = block.expr().map(|e| self.collect_expr(e));
@ -556,6 +558,7 @@ impl ExprCollector<'_> {
let ast_id = self.expander.ast_id(&def);
(TraitLoc { container, ast_id }.intern(self.db).into(), def.name())
}
ast::ModuleItem::ExternBlock(_) => continue, // FIXME: collect from extern blocks
ast::ModuleItem::ImplDef(_)
| ast::ModuleItem::UseItem(_)
| ast::ModuleItem::ExternCrateItem(_)

View file

@ -266,6 +266,10 @@ impl RawItemsCollector {
self.add_macro(current_module, it);
return;
}
ast::ModuleItem::ExternBlock(_) => {
// FIXME: add extern block
return;
}
};
if let Some(name) = name {
let name = name.as_name();

View file

@ -28,7 +28,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
loop {
let segment = path.segment()?;
if segment.has_colon_colon() {
if segment.coloncolon().is_some() {
kind = PathKind::Abs;
}

View file

@ -34,7 +34,7 @@ pub(crate) fn lower_use_tree(
let alias = tree.alias().map(|a| {
a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
});
let is_glob = tree.has_star();
let is_glob = tree.star().is_some();
if let Some(ast_path) = tree.path() {
// Handle self in a path.
// E.g. `use something::{self, <...>}`

View file

@ -84,6 +84,10 @@ impl RawVisibility {
let path = ModPath { kind: PathKind::Super(1), segments: Vec::new() };
RawVisibility::Module(path)
}
ast::VisibilityKind::PubSelf => {
let path = ModPath { kind: PathKind::Plain, segments: Vec::new() };
RawVisibility::Module(path)
}
ast::VisibilityKind::Pub => RawVisibility::Public,
}
}

View file

@ -23,7 +23,7 @@ use insta::assert_snapshot;
use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase};
use ra_syntax::{
algo,
ast::{self, AstNode},
ast::{self, AstNode, AstToken},
};
use stdx::format_to;
@ -101,7 +101,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let node = src_ptr.value.to_node(&src_ptr.file_syntax(&db));
let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.clone()) {
(self_param.self_kw_token().text_range(), "self".to_string())
(self_param.self_kw().unwrap().syntax().text_range(), "self".to_string())
} else {
(src_ptr.value.range(), node.text().to_string().replace("\n", " "))
};

View file

@ -245,7 +245,7 @@ fn opt_fn_ret_type(p: &mut Parser) -> bool {
if p.at(T![->]) {
let m = p.start();
p.bump(T![->]);
types::type_(p);
types::type_no_bounds(p);
m.complete(p, RET_TYPE);
true
} else {

View file

@ -105,6 +105,7 @@ pub enum SyntaxKind {
DEFAULT_KW,
EXISTENTIAL_KW,
UNION_KW,
RAW_KW,
INT_NUMBER,
FLOAT_NUMBER,
CHAR,
@ -258,7 +259,7 @@ impl SyntaxKind {
| IMPL_KW | IN_KW | LET_KW | LOOP_KW | MACRO_KW | MATCH_KW | MOD_KW | MOVE_KW
| MUT_KW | PUB_KW | REF_KW | RETURN_KW | SELF_KW | STATIC_KW | STRUCT_KW | SUPER_KW
| TRAIT_KW | TRUE_KW | TRY_KW | TYPE_KW | UNSAFE_KW | USE_KW | WHERE_KW | WHILE_KW
| AUTO_KW | DEFAULT_KW | EXISTENTIAL_KW | UNION_KW => true,
| AUTO_KW | DEFAULT_KW | EXISTENTIAL_KW | UNION_KW | RAW_KW => true,
_ => false,
}
}
@ -651,4 +652,7 @@ macro_rules! T {
( union ) => {
$crate::SyntaxKind::UNION_KW
};
( raw ) => {
$crate::SyntaxKind::RAW_KW
};
}

View file

@ -7,7 +7,7 @@ use std::{
use itertools::Itertools;
use ra_text_edit::TextEditBuilder;
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_hash::FxHashMap;
use crate::{
AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
@ -72,8 +72,18 @@ pub fn find_covering_element(root: &SyntaxNode, range: TextRange) -> SyntaxEleme
}
pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNode> {
let u_ancestors = u.ancestors().collect::<FxHashSet<SyntaxNode>>();
v.ancestors().find(|it| u_ancestors.contains(it))
if u == v {
return Some(u.clone());
}
let u_depth = u.ancestors().count();
let v_depth = v.ancestors().count();
let keep = u_depth.min(v_depth);
let u_candidates = u.ancestors().skip(u_depth - keep);
let v_canidates = v.ancestors().skip(v_depth - keep);
let (res, _) = u_candidates.zip(v_canidates).find(|(x, y)| x == y)?;
Some(res)
}
pub fn neighbor<T: AstNode>(me: &T, direction: Direction) -> Option<T> {

View file

@ -21,7 +21,7 @@ pub use self::{
AttrKind, FieldKind, PathSegmentKind, SelfParamKind, SlicePatComponents, StructKind,
TypeBoundKind, VisibilityKind,
},
generated::*,
generated::{nodes::*, tokens::*},
tokens::*,
traits::*,
};
@ -64,6 +64,22 @@ pub trait AstToken {
}
}
mod support {
use super::{AstChildren, AstNode, AstToken, SyntaxNode};
pub(super) fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
parent.children().find_map(N::cast)
}
pub(super) fn children<N: AstNode>(parent: &SyntaxNode) -> AstChildren<N> {
AstChildren::new(parent)
}
pub(super) fn token<T: AstToken>(parent: &SyntaxNode) -> Option<T> {
parent.children_with_tokens().filter_map(|it| it.into_token()).find_map(T::cast)
}
}
/// An iterator over `SyntaxNode` children of a particular AST type.
#[derive(Debug, Clone)]
pub struct AstChildren<N> {
@ -271,7 +287,7 @@ where
let pred = predicates.next().unwrap();
let mut bounds = pred.type_bound_list().unwrap().bounds();
assert_eq!("'a", pred.lifetime_token().unwrap().text());
assert_eq!("'a", pred.lifetime().unwrap().text());
assert_bound("'b", bounds.next());
assert_bound("'c", bounds.next());

View file

@ -99,7 +99,7 @@ impl ast::ItemList {
None => match self.l_curly() {
Some(it) => (
" ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(),
InsertPosition::After(it),
InsertPosition::After(it.syntax().clone().into()),
),
None => return self.clone(),
},
@ -109,10 +109,6 @@ impl ast::ItemList {
[ws.ws().into(), item.syntax().clone().into()].into();
self.insert_children(position, to_insert)
}
fn l_curly(&self) -> Option<SyntaxElement> {
self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
}
}
impl ast::RecordFieldList {
@ -147,7 +143,7 @@ impl ast::RecordFieldList {
macro_rules! after_l_curly {
() => {{
let anchor = match self.l_curly() {
Some(it) => it,
Some(it) => it.syntax().clone().into(),
None => return self.clone(),
};
InsertPosition::After(anchor)
@ -189,24 +185,20 @@ impl ast::RecordFieldList {
self.insert_children(position, to_insert)
}
fn l_curly(&self) -> Option<SyntaxElement> {
self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
}
}
impl ast::TypeParam {
#[must_use]
pub fn remove_bounds(&self) -> ast::TypeParam {
let colon = match self.colon_token() {
let colon = match self.colon() {
Some(it) => it,
None => return self.clone(),
};
let end = match self.type_bound_list() {
Some(it) => it.syntax().clone().into(),
None => colon.clone().into(),
None => colon.syntax().clone().into(),
};
self.replace_children(colon.into()..=end, iter::empty())
self.replace_children(colon.syntax().clone().into()..=end, iter::empty())
}
}
@ -305,8 +297,12 @@ impl ast::UseTree {
Some(it) => it,
None => return self.clone(),
};
let use_tree =
make::use_tree(suffix.clone(), self.use_tree_list(), self.alias(), self.has_star());
let use_tree = make::use_tree(
suffix.clone(),
self.use_tree_list(),
self.alias(),
self.star().is_some(),
);
let nested = make::use_tree_list(iter::once(use_tree));
return make::use_tree(prefix.clone(), Some(nested), None, false);

View file

@ -52,6 +52,10 @@ impl ast::RefExpr {
pub fn is_mut(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
}
pub fn raw_token(&self) -> Option<SyntaxToken> {
None // FIXME: implement &raw
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]

View file

@ -4,7 +4,9 @@
use itertools::Itertools;
use crate::{
ast::{self, child_opt, children, AstNode, AttrInput, NameOwner, SyntaxNode},
ast::{
self, child_opt, children, support, AstNode, AstToken, AttrInput, NameOwner, SyntaxNode,
},
SmolStr, SyntaxElement,
SyntaxKind::*,
SyntaxToken, T,
@ -130,13 +132,6 @@ impl ast::PathSegment {
};
Some(res)
}
pub fn has_colon_colon(&self) -> bool {
match self.syntax.first_child_or_token().map(|s| s.kind()) {
Some(T![::]) => true,
_ => false,
}
}
}
impl ast::Path {
@ -154,12 +149,6 @@ impl ast::Module {
}
}
impl ast::UseTree {
pub fn has_star(&self) -> bool {
self.syntax().children_with_tokens().any(|it| it.kind() == T![*])
}
}
impl ast::UseTreeList {
pub fn parent_use_tree(&self) -> ast::UseTree {
self.syntax()
@ -167,20 +156,6 @@ impl ast::UseTreeList {
.and_then(ast::UseTree::cast)
.expect("UseTreeLists are always nested in UseTrees")
}
pub fn l_curly(&self) -> Option<SyntaxToken> {
self.token(T!['{'])
}
pub fn r_curly(&self) -> Option<SyntaxToken> {
self.token(T!['}'])
}
fn token(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|it| it.kind() == kind)
}
}
impl ast::ImplDef {
@ -387,24 +362,9 @@ pub enum SelfParamKind {
}
impl ast::SelfParam {
pub fn self_kw_token(&self) -> SyntaxToken {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|it| it.kind() == T![self])
.expect("invalid tree: self param must have self")
}
pub fn kind(&self) -> SelfParamKind {
let borrowed = self.syntax().children_with_tokens().any(|n| n.kind() == T![&]);
if borrowed {
// check for a `mut` coming after the & -- `mut &self` != `&mut self`
if self
.syntax()
.children_with_tokens()
.skip_while(|n| n.kind() != T![&])
.any(|n| n.kind() == T![mut])
{
if self.amp().is_some() {
if self.amp_mut_kw().is_some() {
SelfParamKind::MutRef
} else {
SelfParamKind::Ref
@ -413,32 +373,23 @@ impl ast::SelfParam {
SelfParamKind::Owned
}
}
}
impl ast::LifetimeParam {
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
/// the "mut" in "mut self", not the one in "&mut self"
pub fn mut_kw(&self) -> Option<ast::MutKw> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|it| it.kind() == LIFETIME)
}
.take_while(|it| it.kind() != T![&])
.find_map(ast::MutKw::cast)
}
impl ast::TypeParam {
pub fn colon_token(&self) -> Option<SyntaxToken> {
/// the "mut" in "&mut self", not the one in "mut self"
pub fn amp_mut_kw(&self) -> Option<ast::MutKw> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|it| it.kind() == T![:])
}
}
impl ast::WherePred {
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|it| it.kind() == LIFETIME)
.skip_while(|it| it.kind() != T![&])
.find_map(ast::MutKw::cast)
}
}
@ -449,7 +400,7 @@ pub enum TypeBoundKind {
/// for<'a> ...
ForType(ast::ForType),
/// 'a
Lifetime(ast::SyntaxToken),
Lifetime(ast::Lifetime),
}
impl ast::TypeBound {
@ -465,21 +416,28 @@ impl ast::TypeBound {
}
}
fn lifetime(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|it| it.kind() == LIFETIME)
pub fn has_question_mark(&self) -> bool {
self.question().is_some()
}
pub fn question_mark_token(&self) -> Option<SyntaxToken> {
pub fn const_question(&self) -> Option<ast::Question> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|it| it.kind() == T![?])
.take_while(|it| it.kind() != T![const])
.find_map(ast::Question::cast)
}
pub fn question(&self) -> Option<ast::Question> {
if self.const_kw().is_some() {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.skip_while(|it| it.kind() != T![const])
.find_map(ast::Question::cast)
} else {
support::token(&self.syntax)
}
pub fn has_question_mark(&self) -> bool {
self.question_mark_token().is_some()
}
}
@ -493,6 +451,7 @@ pub enum VisibilityKind {
In(ast::Path),
PubCrate,
PubSuper,
PubSelf,
Pub,
}
@ -504,6 +463,8 @@ impl ast::Visibility {
VisibilityKind::PubCrate
} else if self.is_pub_super() {
VisibilityKind::PubSuper
} else if self.is_pub_self() {
VisibilityKind::PubSuper
} else {
VisibilityKind::Pub
}
@ -516,6 +477,10 @@ impl ast::Visibility {
fn is_pub_super(&self) -> bool {
self.syntax().children_with_tokens().any(|it| it.kind() == T![super])
}
fn is_pub_self(&self) -> bool {
self.syntax().children_with_tokens().any(|it| it.kind() == T![self])
}
}
impl ast::MacroCall {
@ -528,3 +493,41 @@ impl ast::MacroCall {
}
}
}
impl ast::LifetimeParam {
pub fn lifetime_bounds(&self) -> impl Iterator<Item = ast::Lifetime> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.skip_while(|x| x.kind() != T![:])
.filter_map(ast::Lifetime::cast)
}
}
impl ast::RangePat {
pub fn start(&self) -> Option<ast::Pat> {
self.syntax()
.children_with_tokens()
.take_while(|it| !ast::RangeSeparator::can_cast(it.kind()))
.filter_map(|it| it.into_node())
.find_map(ast::Pat::cast)
}
pub fn end(&self) -> Option<ast::Pat> {
self.syntax()
.children_with_tokens()
.skip_while(|it| !ast::RangeSeparator::can_cast(it.kind()))
.filter_map(|it| it.into_node())
.find_map(ast::Pat::cast)
}
}
impl ast::TokenTree {
pub fn left_delimiter(&self) -> Option<ast::LeftDelimiter> {
self.syntax().first_child_or_token()?.into_token().and_then(ast::LeftDelimiter::cast)
}
pub fn right_delimiter(&self) -> Option<ast::RightDelimiter> {
self.syntax().last_child_or_token()?.into_token().and_then(ast::RightDelimiter::cast)
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
use itertools::Itertools;
use crate::{
ast::{self, child_opt, children, AstChildren, AstNode, AstToken},
ast::{self, child_opt, children, support, AstChildren, AstNode, AstToken},
syntax_node::SyntaxElementChildren,
};
@ -31,6 +31,10 @@ pub trait LoopBodyOwner: AstNode {
fn loop_body(&self) -> Option<ast::BlockExpr> {
child_opt(self)
}
fn label(&self) -> Option<ast::Label> {
child_opt(self)
}
}
pub trait ArgListOwner: AstNode {
@ -65,6 +69,10 @@ pub trait TypeBoundsOwner: AstNode {
fn type_bound_list(&self) -> Option<ast::TypeBoundList> {
child_opt(self)
}
fn colon(&self) -> Option<ast::Colon> {
support::token(self.syntax())
}
}
pub trait AttrsOwner: AstNode {

View file

@ -0,0 +1,61 @@
SOURCE_FILE@[0; 40)
FN_DEF@[0; 39)
FN_KW@[0; 2) "fn"
WHITESPACE@[2; 3) " "
NAME@[3; 4)
IDENT@[3; 4) "f"
TYPE_PARAM_LIST@[4; 7)
L_ANGLE@[4; 5) "<"
TYPE_PARAM@[5; 6)
NAME@[5; 6)
IDENT@[5; 6) "T"
R_ANGLE@[6; 7) ">"
PARAM_LIST@[7; 9)
L_PAREN@[7; 8) "("
R_PAREN@[8; 9) ")"
WHITESPACE@[9; 10) " "
WHERE_CLAUSE@[10; 36)
WHERE_KW@[10; 15) "where"
WHITESPACE@[15; 16) " "
WHERE_PRED@[16; 36)
PATH_TYPE@[16; 17)
PATH@[16; 17)
PATH_SEGMENT@[16; 17)
NAME_REF@[16; 17)
IDENT@[16; 17) "T"
COLON@[17; 18) ":"
WHITESPACE@[18; 19) " "
TYPE_BOUND_LIST@[19; 36)
TYPE_BOUND@[19; 29)
PATH_TYPE@[19; 29)
PATH@[19; 29)
PATH_SEGMENT@[19; 29)
NAME_REF@[19; 21)
IDENT@[19; 21) "Fn"
PARAM_LIST@[21; 23)
L_PAREN@[21; 22) "("
R_PAREN@[22; 23) ")"
WHITESPACE@[23; 24) " "
RET_TYPE@[24; 29)
THIN_ARROW@[24; 26) "->"
WHITESPACE@[26; 27) " "
PATH_TYPE@[27; 29)
PATH@[27; 29)
PATH_SEGMENT@[27; 29)
NAME_REF@[27; 29)
IDENT@[27; 29) "u8"
WHITESPACE@[29; 30) " "
PLUS@[30; 31) "+"
WHITESPACE@[31; 32) " "
TYPE_BOUND@[32; 36)
PATH_TYPE@[32; 36)
PATH@[32; 36)
PATH_SEGMENT@[32; 36)
NAME_REF@[32; 36)
IDENT@[32; 36) "Send"
WHITESPACE@[36; 37) " "
BLOCK_EXPR@[37; 39)
BLOCK@[37; 39)
L_CURLY@[37; 38) "{"
R_CURLY@[38; 39) "}"
WHITESPACE@[39; 40) "\n"

View file

@ -0,0 +1 @@
fn f<T>() where T: Fn() -> u8 + Send {}

View file

@ -70,7 +70,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
"match", "mod", "move", "mut", "pub", "ref", "return", "self", "static", "struct", "super",
"trait", "true", "try", "type", "unsafe", "use", "where", "while",
],
contextual_keywords: &["auto", "default", "existential", "union"],
contextual_keywords: &["auto", "default", "existential", "union", "raw"],
literals: &[
"INT_NUMBER",
"FLOAT_NUMBER",
@ -227,6 +227,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
pub(crate) struct AstSrc<'a> {
pub(crate) nodes: &'a [AstNodeSrc<'a>],
pub(crate) enums: &'a [AstEnumSrc<'a>],
pub(crate) token_enums: &'a [AstEnumSrc<'a>],
}
pub(crate) struct AstNodeSrc<'a> {
@ -297,235 +298,310 @@ macro_rules! ast_enums {
pub(crate) const AST_SRC: AstSrc = AstSrc {
nodes: &ast_nodes! {
struct SourceFile: ModuleItemOwner, FnDefOwner {
struct SourceFile: ModuleItemOwner, FnDefOwner, AttrsOwner {
modules: [Module],
}
struct FnDef: VisibilityOwner, NameOwner, TypeParamsOwner, DocCommentsOwner, AttrsOwner {
Abi,
ConstKw,
DefaultKw,
AsyncKw,
UnsafeKw,
FnKw,
ParamList,
RetType,
body: BlockExpr,
Semi
}
struct RetType { TypeRef }
struct RetType { ThinArrow, TypeRef }
struct StructDef: VisibilityOwner, NameOwner, TypeParamsOwner, AttrsOwner, DocCommentsOwner {
StructKw,
FieldDefList,
Semi
}
struct UnionDef: VisibilityOwner, NameOwner, TypeParamsOwner, AttrsOwner, DocCommentsOwner {
UnionKw,
RecordFieldDefList,
}
struct RecordFieldDefList { fields: [RecordFieldDef] }
struct RecordFieldDefList { LCurly, fields: [RecordFieldDef], RCurly }
struct RecordFieldDef: VisibilityOwner, NameOwner, AttrsOwner, DocCommentsOwner, TypeAscriptionOwner { }
struct TupleFieldDefList { fields: [TupleFieldDef] }
struct TupleFieldDefList { LParen, fields: [TupleFieldDef], RParen }
struct TupleFieldDef: VisibilityOwner, AttrsOwner {
TypeRef,
}
struct EnumDef: VisibilityOwner, NameOwner, TypeParamsOwner, AttrsOwner, DocCommentsOwner {
EnumKw,
variant_list: EnumVariantList,
}
struct EnumVariantList {
LCurly,
variants: [EnumVariant],
RCurly
}
struct EnumVariant: NameOwner, DocCommentsOwner, AttrsOwner {
struct EnumVariant: VisibilityOwner, NameOwner, DocCommentsOwner, AttrsOwner {
FieldDefList,
Eq,
Expr
}
struct TraitDef: VisibilityOwner, NameOwner, AttrsOwner, DocCommentsOwner, TypeParamsOwner, TypeBoundsOwner {
UnsafeKw,
AutoKw,
TraitKw,
ItemList,
}
struct Module: VisibilityOwner, NameOwner, AttrsOwner, DocCommentsOwner {
ModKw,
ItemList,
Semi
}
struct ItemList: FnDefOwner, ModuleItemOwner {
LCurly,
impl_items: [ImplItem],
RCurly
}
struct ConstDef: VisibilityOwner, NameOwner, TypeParamsOwner, AttrsOwner, DocCommentsOwner, TypeAscriptionOwner {
DefaultKw,
ConstKw,
Eq,
body: Expr,
Semi
}
struct StaticDef: VisibilityOwner, NameOwner, TypeParamsOwner, AttrsOwner, DocCommentsOwner, TypeAscriptionOwner {
StaticKw,
MutKw,
Eq,
body: Expr,
Semi
}
struct TypeAliasDef: VisibilityOwner, NameOwner, TypeParamsOwner, AttrsOwner, DocCommentsOwner, TypeBoundsOwner {
DefaultKw,
TypeKw,
Eq,
TypeRef,
Semi
}
struct ImplDef: TypeParamsOwner, AttrsOwner {
DefaultKw,
ConstKw,
UnsafeKw,
ImplKw,
Excl,
ForKw,
ItemList,
}
struct ParenType { TypeRef }
struct TupleType { fields: [TypeRef] }
struct NeverType { }
struct ParenType { LParen, TypeRef, RParen }
struct TupleType { LParen, fields: [TypeRef], RParen }
struct NeverType { Excl }
struct PathType { Path }
struct PointerType { TypeRef }
struct ArrayType { TypeRef, Expr }
struct SliceType { TypeRef }
struct ReferenceType { TypeRef }
struct PlaceholderType { }
struct FnPointerType { ParamList, RetType }
struct ForType { TypeRef }
struct ImplTraitType: TypeBoundsOwner {}
struct DynTraitType: TypeBoundsOwner {}
struct PointerType { Star, ConstKw, TypeRef }
struct ArrayType { LBrack, TypeRef, Semi, Expr, RBrack }
struct SliceType { LBrack, TypeRef, RBrack }
struct ReferenceType { Amp, Lifetime, MutKw, TypeRef }
struct PlaceholderType { Underscore }
struct FnPointerType { Abi, UnsafeKw, FnKw, ParamList, RetType }
struct ForType { ForKw, TypeParamList, TypeRef }
struct ImplTraitType: TypeBoundsOwner { ImplKw }
struct DynTraitType: TypeBoundsOwner { DynKw }
struct TupleExpr { exprs: [Expr] }
struct ArrayExpr { exprs: [Expr] }
struct ParenExpr { Expr }
struct TupleExpr: AttrsOwner { LParen, exprs: [Expr], RParen }
struct ArrayExpr: AttrsOwner { LBrack, exprs: [Expr], Semi, RBrack }
struct ParenExpr: AttrsOwner { LParen, Expr, RParen }
struct PathExpr { Path }
struct LambdaExpr {
struct LambdaExpr: AttrsOwner {
StaticKw,
AsyncKw,
MoveKw,
ParamList,
RetType,
body: Expr,
}
struct IfExpr { Condition }
struct LoopExpr: LoopBodyOwner { }
struct TryBlockExpr { body: BlockExpr }
struct ForExpr: LoopBodyOwner {
struct IfExpr: AttrsOwner { IfKw, Condition }
struct LoopExpr: AttrsOwner, LoopBodyOwner { LoopKw }
struct TryBlockExpr: AttrsOwner { TryKw, body: BlockExpr }
struct ForExpr: AttrsOwner, LoopBodyOwner {
ForKw,
Pat,
InKw,
iterable: Expr,
}
struct WhileExpr: LoopBodyOwner { Condition }
struct ContinueExpr {}
struct BreakExpr { Expr }
struct Label {}
struct BlockExpr { Block }
struct ReturnExpr { Expr }
struct WhileExpr: AttrsOwner, LoopBodyOwner { WhileKw, Condition }
struct ContinueExpr: AttrsOwner { ContinueKw, Lifetime }
struct BreakExpr: AttrsOwner { BreakKw, Lifetime, Expr }
struct Label { Lifetime }
struct BlockExpr: AttrsOwner { Label, UnsafeKw, Block }
struct ReturnExpr: AttrsOwner { Expr }
struct CallExpr: ArgListOwner { Expr }
struct MethodCallExpr: ArgListOwner {
Expr, NameRef, TypeArgList,
struct MethodCallExpr: AttrsOwner, ArgListOwner {
Expr, Dot, NameRef, TypeArgList,
}
struct IndexExpr {}
struct FieldExpr { Expr, NameRef }
struct AwaitExpr { Expr }
struct TryExpr { Expr }
struct CastExpr { Expr, TypeRef }
struct RefExpr { Expr }
struct PrefixExpr { Expr }
struct BoxExpr { Expr }
struct RangeExpr {}
struct BinExpr {}
struct Literal {}
struct IndexExpr: AttrsOwner { LBrack, RBrack }
struct FieldExpr: AttrsOwner { Expr, Dot, NameRef }
struct AwaitExpr: AttrsOwner { Expr, Dot, AwaitKw }
struct TryExpr: AttrsOwner { TryKw, Expr }
struct CastExpr: AttrsOwner { Expr, AsKw, TypeRef }
struct RefExpr: AttrsOwner { Amp, RawKw, MutKw, Expr }
struct PrefixExpr: AttrsOwner { PrefixOp, Expr }
struct BoxExpr: AttrsOwner { BoxKw, Expr }
struct RangeExpr: AttrsOwner { RangeOp }
struct BinExpr: AttrsOwner { BinOp }
struct Literal { LiteralToken }
struct MatchExpr { Expr, MatchArmList }
struct MatchArmList: AttrsOwner { arms: [MatchArm] }
struct MatchExpr: AttrsOwner { MatchKw, Expr, MatchArmList }
struct MatchArmList: AttrsOwner { LCurly, arms: [MatchArm], RCurly }
struct MatchArm: AttrsOwner {
pat: Pat,
guard: MatchGuard,
FatArrow,
Expr,
}
struct MatchGuard { Expr }
struct MatchGuard { IfKw, Expr }
struct RecordLit { Path, RecordFieldList}
struct RecordFieldList {
LCurly,
fields: [RecordField],
Dotdot,
spread: Expr,
RCurly
}
struct RecordField { NameRef, Expr }
struct RecordField: AttrsOwner { NameRef, Colon, Expr }
struct OrPat { pats: [Pat] }
struct ParenPat { Pat }
struct RefPat { Pat }
struct BoxPat { Pat }
struct BindPat: NameOwner { Pat }
struct PlaceholderPat { }
struct DotDotPat { }
struct ParenPat { LParen, Pat, RParen }
struct RefPat { Amp, MutKw, Pat }
struct BoxPat { BoxKw, Pat }
struct BindPat: AttrsOwner, NameOwner { RefKw, MutKw, Pat }
struct PlaceholderPat { Underscore }
struct DotDotPat { Dotdot }
struct PathPat { Path }
struct SlicePat { args: [Pat] }
struct RangePat {}
struct SlicePat { LBrack, args: [Pat], RBrack }
struct RangePat { RangeSeparator }
struct LiteralPat { Literal }
struct MacroPat { MacroCall }
struct RecordPat { RecordFieldPatList, Path }
struct RecordFieldPatList {
LCurly,
pats: [RecordInnerPat],
record_field_pats: [RecordFieldPat],
bind_pats: [BindPat],
Dotdot,
RCurly
}
struct RecordFieldPat: NameOwner { Pat }
struct RecordFieldPat: AttrsOwner, NameOwner { Colon, Pat }
struct TupleStructPat { Path, args: [Pat] }
struct TuplePat { args: [Pat] }
struct TupleStructPat { Path, LParen, args: [Pat], RParen }
struct TuplePat { LParen, args: [Pat], RParen }
struct Visibility {}
struct Name {}
struct NameRef {}
struct Visibility { PubKw, SuperKw, SelfKw, CrateKw }
struct Name { Ident }
struct NameRef { NameRefToken }
struct MacroCall: NameOwner, AttrsOwner,DocCommentsOwner {
TokenTree, Path
Path, Excl, TokenTree, Semi
}
struct Attr { Path, input: AttrInput }
struct Attr { Pound, Excl, LBrack, Path, Eq, input: AttrInput, RBrack }
struct TokenTree {}
struct TypeParamList {
LAngle,
generic_params: [GenericParam],
type_params: [TypeParam],
lifetime_params: [LifetimeParam],
const_params: [ConstParam],
RAngle
}
struct TypeParam: NameOwner, AttrsOwner, TypeBoundsOwner {
Eq,
default_type: TypeRef,
}
struct ConstParam: NameOwner, AttrsOwner, TypeAscriptionOwner {
Eq,
default_val: Expr,
}
struct LifetimeParam: AttrsOwner { }
struct TypeBound { TypeRef}
struct LifetimeParam: AttrsOwner { Lifetime}
struct TypeBound { Lifetime, /* Question, */ ConstKw, /* Question, */ TypeRef}
struct TypeBoundList { bounds: [TypeBound] }
struct WherePred: TypeBoundsOwner { TypeRef }
struct WhereClause { predicates: [WherePred] }
struct ExprStmt { Expr }
struct LetStmt: TypeAscriptionOwner {
struct WherePred: TypeBoundsOwner { Lifetime, TypeRef }
struct WhereClause { WhereKw, predicates: [WherePred] }
struct Abi { String }
struct ExprStmt: AttrsOwner { Expr, Semi }
struct LetStmt: AttrsOwner, TypeAscriptionOwner {
LetKw,
Pat,
Eq,
initializer: Expr,
}
struct Condition { Pat, Expr }
struct Condition { LetKw, Pat, Eq, Expr }
struct Block: AttrsOwner, ModuleItemOwner {
LCurly,
statements: [Stmt],
Expr,
RCurly,
}
struct ParamList {
LParen,
SelfParam,
params: [Param],
RParen
}
struct SelfParam: TypeAscriptionOwner, AttrsOwner { }
struct SelfParam: TypeAscriptionOwner, AttrsOwner { Amp, Lifetime, SelfKw }
struct Param: TypeAscriptionOwner, AttrsOwner {
Pat,
Dotdotdot
}
struct UseItem: AttrsOwner, VisibilityOwner {
UseKw,
UseTree,
}
struct UseTree {
Path, UseTreeList, Alias
Path, Star, UseTreeList, Alias
}
struct Alias: NameOwner { }
struct UseTreeList { use_trees: [UseTree] }
struct Alias: NameOwner { AsKw }
struct UseTreeList { LCurly, use_trees: [UseTree], RCurly }
struct ExternCrateItem: AttrsOwner, VisibilityOwner {
NameRef, Alias,
ExternKw, CrateKw, NameRef, Alias,
}
struct ArgList {
LParen,
args: [Expr],
RParen
}
struct Path {
segment: PathSegment,
qualifier: Path,
}
struct PathSegment {
NameRef, TypeArgList, ParamList, RetType, PathType,
Coloncolon, LAngle, NameRef, TypeArgList, ParamList, RetType, PathType, RAngle
}
struct TypeArgList {
Coloncolon,
LAngle,
generic_args: [GenericArg],
type_args: [TypeArg],
lifetime_args: [LifetimeArg],
assoc_type_args: [AssocTypeArg],
const_arg: [ConstArg],
const_args: [ConstArg],
RAngle
}
struct TypeArg { TypeRef }
struct AssocTypeArg { NameRef, TypeRef }
struct LifetimeArg {}
struct ConstArg { Literal, BlockExpr }
struct AssocTypeArg : TypeBoundsOwner { NameRef, Eq, TypeRef }
struct LifetimeArg { Lifetime }
struct ConstArg { Literal, Eq, BlockExpr }
struct MacroItems: ModuleItemOwner, FnDefOwner { }
@ -533,12 +609,44 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
statements: [Stmt],
Expr,
}
struct ExternItemList: FnDefOwner, ModuleItemOwner {
LCurly,
extern_items: [ExternItem],
RCurly
}
struct ExternBlock {
Abi,
ExternItemList
}
struct MetaItem {
Path, Eq, AttrInput, nested_meta_items: [MetaItem]
}
struct MacroDef {
Name, TokenTree
}
},
enums: &ast_enums! {
enum NominalDef: NameOwner, TypeParamsOwner, AttrsOwner {
StructDef, EnumDef, UnionDef,
}
enum GenericParam {
LifetimeParam,
TypeParam,
ConstParam
}
enum GenericArg {
LifetimeArg,
TypeArg,
ConstArg,
AssocTypeArg
}
enum TypeRef {
ParenType,
TupleType,
@ -555,7 +663,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
DynTraitType,
}
enum ModuleItem: AttrsOwner, VisibilityOwner {
enum ModuleItem: NameOwner, AttrsOwner, VisibilityOwner {
StructDef,
UnionDef,
EnumDef,
@ -569,13 +677,20 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
StaticDef,
Module,
MacroCall,
ExternBlock
}
enum ImplItem: AttrsOwner {
FnDef, TypeAliasDef, ConstDef,
/* impl blocks can also contain MacroCall */
enum ImplItem: NameOwner, AttrsOwner {
FnDef, TypeAliasDef, ConstDef
}
enum Expr {
/* extern blocks can also contain MacroCall */
enum ExternItem: NameOwner, AttrsOwner, VisibilityOwner {
FnDef, StaticDef
}
enum Expr: AttrsOwner {
TupleExpr,
ArrayExpr,
ParenExpr,
@ -627,7 +742,88 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
MacroPat,
}
enum RecordInnerPat {
RecordFieldPat,
BindPat
}
enum AttrInput { Literal, TokenTree }
enum Stmt { ExprStmt, LetStmt }
enum Stmt {
LetStmt,
ExprStmt,
// macro calls are parsed as expression statements */
}
enum FieldDefList {
RecordFieldDefList,
TupleFieldDefList,
}
},
token_enums: &ast_enums! {
enum LeftDelimiter { LParen, LBrack, LCurly }
enum RightDelimiter { RParen, RBrack, RCurly }
enum RangeSeparator { Dotdot, Dotdotdot, Dotdoteq}
enum BinOp {
Pipepipe,
Ampamp,
Eqeq,
Neq,
Lteq,
Gteq,
LAngle,
RAngle,
Plus,
Star,
Minus,
Slash,
Percent,
Shl,
Shr,
Caret,
Pipe,
Amp,
Eq,
Pluseq,
Slasheq,
Stareq,
Percenteq,
Shreq,
Shleq,
Minuseq,
Pipeeq,
Ampeq,
Careteq,
}
enum PrefixOp {
Minus,
Excl,
Star
}
enum RangeOp {
Dotdot,
Dotdoteq
}
enum LiteralToken {
IntNumber,
FloatNumber,
String,
RawString,
TrueKw,
FalseKw,
ByteString,
RawByteString,
Char,
Byte
}
enum NameRefToken {
Ident,
IntNumber
}
},
};

View file

@ -22,8 +22,9 @@ const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar";
const OK_INLINE_TESTS_DIR: &str = "crates/ra_syntax/test_data/parser/inline/ok";
const ERR_INLINE_TESTS_DIR: &str = "crates/ra_syntax/test_data/parser/inline/err";
pub const SYNTAX_KINDS: &str = "crates/ra_parser/src/syntax_kind/generated.rs";
pub const AST: &str = "crates/ra_syntax/src/ast/generated.rs";
const SYNTAX_KINDS: &str = "crates/ra_parser/src/syntax_kind/generated.rs";
const AST_NODES: &str = "crates/ra_syntax/src/ast/generated/nodes.rs";
const AST_TOKENS: &str = "crates/ra_syntax/src/ast/generated/tokens.rs";
const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers";
const ASSISTS_TESTS: &str = "crates/ra_assists/src/doc_tests/generated.rs";

View file

@ -3,10 +3,13 @@
//! Specifically, it generates the `SyntaxKind` enum and a number of newtype
//! wrappers around `SyntaxNode` which implement `ra_syntax::AstNode`.
use std::{
borrow::Cow,
collections::{BTreeSet, HashSet},
};
use proc_macro2::{Punct, Spacing};
use quote::{format_ident, quote};
use std::borrow::Cow;
use std::collections::{BTreeSet, HashMap, HashSet};
use crate::{
ast_src::{AstSrc, FieldSrc, KindsSrc, AST_SRC, KINDS_SRC},
@ -19,9 +22,13 @@ pub fn generate_syntax(mode: Mode) -> Result<()> {
let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?;
update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?;
let ast_file = project_root().join(codegen::AST);
let ast = generate_ast(KINDS_SRC, AST_SRC)?;
update(ast_file.as_path(), &ast, mode)?;
let ast_nodes_file = project_root().join(codegen::AST_NODES);
let contents = generate_nodes(KINDS_SRC, AST_SRC)?;
update(ast_nodes_file.as_path(), &contents, mode)?;
let ast_tokens_file = project_root().join(codegen::AST_TOKENS);
let contents = generate_tokens(KINDS_SRC, AST_SRC)?;
update(ast_tokens_file.as_path(), &contents, mode)?;
Ok(())
}
@ -33,7 +40,7 @@ struct ElementKinds {
has_tokens: bool,
}
fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
fn generate_tokens(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
let all_token_kinds: Vec<_> = kinds
.punct
.into_iter()
@ -51,46 +58,6 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
.chain(kinds.tokens.into_iter().copied().map(|x| x.into()))
.collect();
let mut element_kinds_map = HashMap::new();
for kind in &all_token_kinds {
let kind = &**kind;
let name = to_pascal_case(kind);
element_kinds_map.insert(
name,
ElementKinds {
kinds: Some(format_ident!("{}", kind)).into_iter().collect(),
has_nodes: false,
has_tokens: true,
},
);
}
for kind in kinds.nodes {
let name = to_pascal_case(kind);
element_kinds_map.insert(
name,
ElementKinds {
kinds: Some(format_ident!("{}", *kind)).into_iter().collect(),
has_nodes: true,
has_tokens: false,
},
);
}
for en in grammar.enums {
let mut element_kinds: ElementKinds = Default::default();
for variant in en.variants {
if let Some(variant_element_kinds) = element_kinds_map.get(*variant) {
element_kinds.kinds.extend(variant_element_kinds.kinds.iter().cloned());
element_kinds.has_tokens |= variant_element_kinds.has_tokens;
element_kinds.has_nodes |= variant_element_kinds.has_nodes;
} else {
panic!("Enum variant has type that does not exist or was not declared before the enum: {}", *variant);
}
}
element_kinds_map.insert(en.name.to_string(), element_kinds);
}
let tokens = all_token_kinds.iter().map(|kind_str| {
let kind_str = &**kind_str;
let kind = format_ident!("{}", kind_str);
@ -108,12 +75,7 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
}
impl AstToken for #name {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
#kind => true,
_ => false,
}
}
fn can_cast(kind: SyntaxKind) -> bool { kind == #kind }
fn cast(syntax: SyntaxToken) -> Option<Self> {
if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
}
@ -122,6 +84,99 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
}
});
let enums = grammar.token_enums.iter().map(|en| {
let variants = en.variants.iter().map(|var| format_ident!("{}", var)).collect::<Vec<_>>();
let name = format_ident!("{}", en.name);
let kinds = variants
.iter()
.map(|name| format_ident!("{}", to_upper_snake_case(&name.to_string())))
.collect::<Vec<_>>();
assert!(en.traits.is_empty());
quote! {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum #name {
#(#variants(#variants),)*
}
#(
impl From<#variants> for #name {
fn from(node: #variants) -> #name {
#name::#variants(node)
}
}
)*
impl std::fmt::Display for #name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl AstToken for #name {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
#(#kinds)|* => true,
_ => false,
}
}
fn cast(syntax: SyntaxToken) -> Option<Self> {
let res = match syntax.kind() {
#(
#kinds => #name::#variants(#variants { syntax }),
)*
_ => return None,
};
Some(res)
}
fn syntax(&self) -> &SyntaxToken {
match self {
#(
#name::#variants(it) => &it.syntax,
)*
}
}
}
}
});
crate::reformat(quote! {
use crate::{SyntaxToken, SyntaxKind::{self, *}, ast::AstToken};
#(#tokens)*
#(#enums)*
})
}
fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
let all_token_kinds: Vec<_> = kinds
.punct
.into_iter()
.map(|(_, kind)| kind)
.copied()
.map(|x| x.into())
.chain(
kinds
.keywords
.into_iter()
.chain(kinds.contextual_keywords.into_iter())
.map(|name| Cow::Owned(format!("{}_KW", to_upper_snake_case(&name)))),
)
.chain(kinds.literals.into_iter().copied().map(|x| x.into()))
.chain(kinds.tokens.into_iter().copied().map(|x| x.into()))
.collect();
let mut token_kinds = HashSet::new();
for kind in &all_token_kinds {
let kind = &**kind;
let name = to_pascal_case(kind);
token_kinds.insert(name);
}
for en in grammar.token_enums {
token_kinds.insert(en.name.to_string());
}
let nodes = grammar.nodes.iter().map(|node| {
let name = format_ident!("{}", node.name);
let kind = format_ident!("{}", to_upper_snake_case(&name.to_string()));
@ -146,14 +201,23 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
FieldSrc::Many(_) => {
quote! {
pub fn #method_name(&self) -> AstChildren<#ty> {
AstChildren::new(&self.syntax)
support::children(&self.syntax)
}
}
}
FieldSrc::Optional(_) | FieldSrc::Shorthand => {
let is_token = token_kinds.contains(&ty.to_string());
if is_token {
quote! {
pub fn #method_name(&self) -> Option<#ty> {
AstChildren::new(&self.syntax).next()
support::token(&self.syntax)
}
}
} else {
quote! {
pub fn #method_name(&self) -> Option<#ty> {
support::child(&self.syntax)
}
}
}
}
@ -166,18 +230,9 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
pub(crate) syntax: SyntaxNode,
}
impl std::fmt::Display for #name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl AstNode for #name {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
#kind => true,
_ => false,
}
kind == #kind
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
@ -219,12 +274,6 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
}
)*
impl std::fmt::Display for #name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl AstNode for #name {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
@ -249,10 +298,26 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
}
}
}
#(#traits)*
}
});
let displays = grammar
.enums
.iter()
.map(|it| format_ident!("{}", it.name))
.chain(grammar.nodes.iter().map(|it| format_ident!("{}", it.name)))
.map(|name| {
quote! {
impl std::fmt::Display for #name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
}
});
let defined_nodes: HashSet<_> = grammar.nodes.iter().map(|node| node.name).collect();
for node in kinds
@ -265,15 +330,16 @@ fn generate_ast(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
}
let ast = quote! {
#[allow(unused_imports)]
use crate::{
SyntaxNode, SyntaxToken, SyntaxElement, NodeOrToken, SyntaxKind::{self, *},
ast::{self, AstNode, AstToken, AstChildren},
SyntaxNode, SyntaxKind::{self, *},
ast::{self, AstNode, AstChildren, support},
};
#(#tokens)*
use super::tokens::*;
#(#nodes)*
#(#enums)*
#(#displays)*
};
let pretty = crate::reformat(ast)?;

View file

@ -67,6 +67,7 @@ fn reformat(text: impl std::fmt::Display) -> Result<String> {
let mut rustfmt = Command::new("rustup")
.args(&["run", TOOLCHAIN, "--", "rustfmt", "--config-path"])
.arg(project_root().join("rustfmt.toml"))
.args(&["--config", "fn_single_line=true"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;