Skip to content

Commit 791a632

Browse files
Move some more AST makers to the quote macro
And implement addons as necessary. There are many more makers to be moved, and I'm not completely satisfied with this (due to the ease of making a mistake in the AST structure, and slightly less but also because of the need to remember whitespaces), but this is already enough to see how this will look like.
1 parent abd7263 commit 791a632

File tree

4 files changed

+58
-53
lines changed

4 files changed

+58
-53
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ new_ret_no_self = "allow"
203203
useless_asref = "allow"
204204
# Has false positives
205205
assigning_clones = "allow"
206+
# Does not work with macros
207+
vec_init_then_push = "allow"
206208

207209
## Following lints should be tackled at some point
208210
too_many_arguments = "allow"

crates/ide-assists/src/handlers/generate_fn_type_alias.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>)
8585

8686
let is_unsafe = func_node.unsafe_token().is_some();
8787
let ty = make::ty_fn_ptr(
88-
None,
8988
is_unsafe,
9089
func_node.abi(),
9190
fn_params_vec.into_iter(),

crates/syntax/src/ast/make.rs

Lines changed: 33 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
//! Keep in mind that `from_text` functions should be kept private. The public
99
//! API should require to assemble every node piecewise. The trick of
1010
//! `parse(format!())` we use internally is an implementation detail -- long
11-
//! term, it will be replaced with direct tree manipulation.
11+
//! term, it will be replaced with `quote!`. Do not add more usages to `from_text` -
12+
//! use `quote!` instead.
1213
1314
mod quote;
1415

@@ -120,7 +121,11 @@ pub fn name(name: &str) -> ast::Name {
120121
}
121122
pub fn name_ref(name_ref: &str) -> ast::NameRef {
122123
let raw_escape = raw_ident_esc(name_ref);
123-
ast_from_text(&format!("fn f() {{ {raw_escape}{name_ref}; }}"))
124+
quote! {
125+
NameRef {
126+
[IDENT format!("{raw_escape}{name_ref}")]
127+
}
128+
}
124129
}
125130
fn raw_ident_esc(ident: &str) -> &'static str {
126131
if is_raw_identifier(ident, Edition::CURRENT) {
@@ -137,7 +142,11 @@ pub fn lifetime(text: &str) -> ast::Lifetime {
137142
tmp = format!("'{text}");
138143
text = &tmp;
139144
}
140-
ast_from_text(&format!("fn f<{text}>() {{ }}"))
145+
quote! {
146+
Lifetime {
147+
[LIFETIME_IDENT text]
148+
}
149+
}
141150
}
142151

143152
// FIXME: replace stringly-typed constructor with a family of typed ctors, a-la
@@ -177,63 +186,37 @@ pub fn ty_alias(
177186
where_clause: Option<ast::WhereClause>,
178187
assignment: Option<(ast::Type, Option<ast::WhereClause>)>,
179188
) -> ast::TypeAlias {
180-
let mut s = String::new();
181-
s.push_str(&format!("type {ident}"));
182-
183-
if let Some(list) = generic_param_list {
184-
s.push_str(&list.to_string());
185-
}
186-
187-
if let Some(list) = type_param_bounds {
188-
s.push_str(&format!(" : {list}"));
189-
}
190-
191-
if let Some(cl) = where_clause {
192-
s.push_str(&format!(" {cl}"));
193-
}
194-
195-
if let Some(exp) = assignment {
196-
if let Some(cl) = exp.1 {
197-
s.push_str(&format!(" = {} {cl}", exp.0));
198-
} else {
199-
s.push_str(&format!(" = {}", exp.0));
189+
let (assignment_ty, assignment_where) = assignment.unzip();
190+
let assignment_where = assignment_where.flatten();
191+
quote! {
192+
TypeAlias {
193+
[type] " "
194+
Name { [IDENT ident] }
195+
#generic_param_list
196+
#(" " [:] " " #type_param_bounds)*
197+
#(" " #where_clause)*
198+
#(" " [=] " " #assignment_ty)*
199+
#(" " #assignment_where)*
200+
[;]
200201
}
201202
}
202-
203-
s.push(';');
204-
ast_from_text(&s)
205203
}
206204

207205
pub fn ty_fn_ptr<I: Iterator<Item = Param>>(
208-
for_lifetime_list: Option<ast::GenericParamList>,
209206
is_unsafe: bool,
210207
abi: Option<ast::Abi>,
211-
params: I,
208+
mut params: I,
212209
ret_type: Option<ast::RetType>,
213210
) -> ast::FnPtrType {
214-
let mut s = String::from("type __ = ");
215-
216-
if let Some(list) = for_lifetime_list {
217-
format_to!(s, "for{} ", list);
218-
}
219-
220-
if is_unsafe {
221-
s.push_str("unsafe ");
222-
}
223-
224-
if let Some(abi) = abi {
225-
format_to!(s, "{} ", abi)
226-
}
227-
228-
s.push_str("fn");
229-
230-
format_to!(s, "({})", params.map(|p| p.to_string()).join(", "));
231-
232-
if let Some(ret_type) = ret_type {
233-
format_to!(s, " {}", ret_type);
211+
let is_unsafe = is_unsafe.then_some(());
212+
let first_param = params.next();
213+
quote! {
214+
FnPtrType {
215+
#(#is_unsafe [unsafe] " ")* #(#abi " ")* [fn]
216+
['('] #first_param #([,] " " #params)* [')']
217+
#(" " #ret_type)*
218+
}
234219
}
235-
236-
ast_from_text(&s)
237220
}
238221

239222
pub fn assoc_item_list() -> ast::AssocItemList {

crates/syntax/src/ast/make/quote.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ macro_rules! quote_impl_ {
2727
$crate::ast::make::quote::quote_impl!( @append $children $($rest)* );
2828
};
2929

30+
( @append $children:ident
31+
[ $token_kind:ident $token_text:expr ]
32+
$($rest:tt)*
33+
) => {
34+
$children.push($crate::ast::make::quote::NodeOrToken::Token(
35+
$crate::ast::make::quote::GreenToken::new(
36+
$crate::ast::make::quote::RSyntaxKind($crate::SyntaxKind::$token_kind as u16),
37+
&$token_text,
38+
),
39+
));
40+
$crate::ast::make::quote::quote_impl!( @append $children $($rest)* );
41+
};
42+
3043
( @append $children:ident
3144
[$($token:tt)+]
3245
$($rest:tt)*
@@ -115,7 +128,9 @@ pub(crate) use quote_impl_ as quote_impl;
115128
/// A `quote!`-like API for crafting AST nodes.
116129
///
117130
/// Syntax: AST nodes are created with `Node { children }`, where `Node` is the node name in `ast` (`ast::Node`).
118-
/// Tokens are creates with their syntax enclosed by brackets, e.g. `[::]` or `['{']`. Whitespaces can be added
131+
/// Tokens are creates with their syntax enclosed by brackets, e.g. `[::]` or `['{']`. Alternatively, tokens can
132+
/// be created with the syntax `[token_kind token_text]`, where `token_kind` is a variant of `SyntaxKind` (e.g.
133+
/// `IDENT`) and `token_text` is an expression producing `String` or `&str`. Whitespaces can be added
119134
/// as string literals (i.e. `"\n "` is a whitespace token). Interpolation is allowed with `#` (`#variable`),
120135
/// from `AstNode`s and `Option`s of them. Repetition is also supported, with only one repeating variable
121136
/// and no separator (`#("\n" #variable [>])*`), for any `IntoIterator`. Note that `Option`s are also `IntoIterator`,
@@ -126,6 +141,7 @@ pub(crate) use quote_impl_ as quote_impl;
126141
/// Be careful to closely match the Ungrammar AST, there is no validation for this!
127142
macro_rules! quote_ {
128143
( $root:ident { $($tree:tt)* } ) => {{
144+
#[allow(unused_mut)]
129145
let mut root = ::std::vec::Vec::<$crate::ast::make::quote::NodeOrToken<
130146
$crate::ast::make::quote::GreenNode,
131147
$crate::ast::make::quote::GreenToken,
@@ -146,7 +162,7 @@ pub(crate) trait ToNodeChild {
146162

147163
impl<N: AstNode> ToNodeChild for N {
148164
fn append_node_child(self, children: &mut Vec<NodeOrToken<GreenNode, GreenToken>>) {
149-
children.push(self.syntax().clone_subtree().green().to_owned().into());
165+
children.push((*self.syntax().clone_subtree().green()).to_owned().into());
150166
}
151167
}
152168

@@ -158,6 +174,11 @@ impl<C: ToNodeChild> ToNodeChild for Option<C> {
158174
}
159175
}
160176

177+
// This is useful when you want conditionally, based on some `bool`, to emit some code.
178+
impl ToNodeChild for () {
179+
fn append_node_child(self, _children: &mut Vec<NodeOrToken<GreenNode, GreenToken>>) {}
180+
}
181+
161182
pub(crate) const fn verify_only_whitespaces(text: &str) {
162183
let text = text.as_bytes();
163184
let mut i = 0;

0 commit comments

Comments
 (0)