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.
This commit is contained in:
Chayim Refael Friedman 2024-12-30 05:46:06 +02:00
parent abd7263179
commit 791a63255b
4 changed files with 58 additions and 53 deletions

View file

@ -203,6 +203,8 @@ new_ret_no_self = "allow"
useless_asref = "allow"
# Has false positives
assigning_clones = "allow"
# Does not work with macros
vec_init_then_push = "allow"
## Following lints should be tackled at some point
too_many_arguments = "allow"

View file

@ -85,7 +85,6 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>)
let is_unsafe = func_node.unsafe_token().is_some();
let ty = make::ty_fn_ptr(
None,
is_unsafe,
func_node.abi(),
fn_params_vec.into_iter(),

View file

@ -8,7 +8,8 @@
//! Keep in mind that `from_text` functions should be kept private. The public
//! API should require to assemble every node piecewise. The trick of
//! `parse(format!())` we use internally is an implementation detail -- long
//! term, it will be replaced with direct tree manipulation.
//! term, it will be replaced with `quote!`. Do not add more usages to `from_text` -
//! use `quote!` instead.
mod quote;
@ -120,7 +121,11 @@ pub fn name(name: &str) -> ast::Name {
}
pub fn name_ref(name_ref: &str) -> ast::NameRef {
let raw_escape = raw_ident_esc(name_ref);
ast_from_text(&format!("fn f() {{ {raw_escape}{name_ref}; }}"))
quote! {
NameRef {
[IDENT format!("{raw_escape}{name_ref}")]
}
}
}
fn raw_ident_esc(ident: &str) -> &'static str {
if is_raw_identifier(ident, Edition::CURRENT) {
@ -137,7 +142,11 @@ pub fn lifetime(text: &str) -> ast::Lifetime {
tmp = format!("'{text}");
text = &tmp;
}
ast_from_text(&format!("fn f<{text}>() {{ }}"))
quote! {
Lifetime {
[LIFETIME_IDENT text]
}
}
}
// FIXME: replace stringly-typed constructor with a family of typed ctors, a-la
@ -177,63 +186,37 @@ pub fn ty_alias(
where_clause: Option<ast::WhereClause>,
assignment: Option<(ast::Type, Option<ast::WhereClause>)>,
) -> ast::TypeAlias {
let mut s = String::new();
s.push_str(&format!("type {ident}"));
if let Some(list) = generic_param_list {
s.push_str(&list.to_string());
}
if let Some(list) = type_param_bounds {
s.push_str(&format!(" : {list}"));
}
if let Some(cl) = where_clause {
s.push_str(&format!(" {cl}"));
}
if let Some(exp) = assignment {
if let Some(cl) = exp.1 {
s.push_str(&format!(" = {} {cl}", exp.0));
} else {
s.push_str(&format!(" = {}", exp.0));
let (assignment_ty, assignment_where) = assignment.unzip();
let assignment_where = assignment_where.flatten();
quote! {
TypeAlias {
[type] " "
Name { [IDENT ident] }
#generic_param_list
#(" " [:] " " #type_param_bounds)*
#(" " #where_clause)*
#(" " [=] " " #assignment_ty)*
#(" " #assignment_where)*
[;]
}
}
s.push(';');
ast_from_text(&s)
}
pub fn ty_fn_ptr<I: Iterator<Item = Param>>(
for_lifetime_list: Option<ast::GenericParamList>,
is_unsafe: bool,
abi: Option<ast::Abi>,
params: I,
mut params: I,
ret_type: Option<ast::RetType>,
) -> ast::FnPtrType {
let mut s = String::from("type __ = ");
if let Some(list) = for_lifetime_list {
format_to!(s, "for{} ", list);
let is_unsafe = is_unsafe.then_some(());
let first_param = params.next();
quote! {
FnPtrType {
#(#is_unsafe [unsafe] " ")* #(#abi " ")* [fn]
['('] #first_param #([,] " " #params)* [')']
#(" " #ret_type)*
}
}
if is_unsafe {
s.push_str("unsafe ");
}
if let Some(abi) = abi {
format_to!(s, "{} ", abi)
}
s.push_str("fn");
format_to!(s, "({})", params.map(|p| p.to_string()).join(", "));
if let Some(ret_type) = ret_type {
format_to!(s, " {}", ret_type);
}
ast_from_text(&s)
}
pub fn assoc_item_list() -> ast::AssocItemList {

View file

@ -27,6 +27,19 @@ macro_rules! quote_impl_ {
$crate::ast::make::quote::quote_impl!( @append $children $($rest)* );
};
( @append $children:ident
[ $token_kind:ident $token_text:expr ]
$($rest:tt)*
) => {
$children.push($crate::ast::make::quote::NodeOrToken::Token(
$crate::ast::make::quote::GreenToken::new(
$crate::ast::make::quote::RSyntaxKind($crate::SyntaxKind::$token_kind as u16),
&$token_text,
),
));
$crate::ast::make::quote::quote_impl!( @append $children $($rest)* );
};
( @append $children:ident
[$($token:tt)+]
$($rest:tt)*
@ -115,7 +128,9 @@ pub(crate) use quote_impl_ as quote_impl;
/// A `quote!`-like API for crafting AST nodes.
///
/// Syntax: AST nodes are created with `Node { children }`, where `Node` is the node name in `ast` (`ast::Node`).
/// Tokens are creates with their syntax enclosed by brackets, e.g. `[::]` or `['{']`. Whitespaces can be added
/// Tokens are creates with their syntax enclosed by brackets, e.g. `[::]` or `['{']`. Alternatively, tokens can
/// be created with the syntax `[token_kind token_text]`, where `token_kind` is a variant of `SyntaxKind` (e.g.
/// `IDENT`) and `token_text` is an expression producing `String` or `&str`. Whitespaces can be added
/// as string literals (i.e. `"\n "` is a whitespace token). Interpolation is allowed with `#` (`#variable`),
/// from `AstNode`s and `Option`s of them. Repetition is also supported, with only one repeating variable
/// 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;
/// Be careful to closely match the Ungrammar AST, there is no validation for this!
macro_rules! quote_ {
( $root:ident { $($tree:tt)* } ) => {{
#[allow(unused_mut)]
let mut root = ::std::vec::Vec::<$crate::ast::make::quote::NodeOrToken<
$crate::ast::make::quote::GreenNode,
$crate::ast::make::quote::GreenToken,
@ -146,7 +162,7 @@ pub(crate) trait ToNodeChild {
impl<N: AstNode> ToNodeChild for N {
fn append_node_child(self, children: &mut Vec<NodeOrToken<GreenNode, GreenToken>>) {
children.push(self.syntax().clone_subtree().green().to_owned().into());
children.push((*self.syntax().clone_subtree().green()).to_owned().into());
}
}
@ -158,6 +174,11 @@ impl<C: ToNodeChild> ToNodeChild for Option<C> {
}
}
// This is useful when you want conditionally, based on some `bool`, to emit some code.
impl ToNodeChild for () {
fn append_node_child(self, _children: &mut Vec<NodeOrToken<GreenNode, GreenToken>>) {}
}
pub(crate) const fn verify_only_whitespaces(text: &str) {
let text = text.as_bytes();
let mut i = 0;