Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--Cargo.toml2
-rw-r--r--crates/ide-assists/src/handlers/generate_fn_type_alias.rs1
-rw-r--r--crates/syntax/src/ast/make.rs83
-rw-r--r--crates/syntax/src/ast/make/quote.rs25
4 files changed, 58 insertions, 53 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 7f3abcccc4..e303b3c110 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
index f4b4c22d98..9d01ec00f8 100644
--- a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
+++ b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
@@ -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(),
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 059028bea8..76b39c3b73 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -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);
- }
-
- 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);
+ let is_unsafe = is_unsafe.then_some(());
+ let first_param = params.next();
+ quote! {
+ FnPtrType {
+ #(#is_unsafe [unsafe] " ")* #(#abi " ")* [fn]
+ ['('] #first_param #([,] " " #params)* [')']
+ #(" " #ret_type)*
+ }
}
-
- ast_from_text(&s)
}
pub fn assoc_item_list() -> ast::AssocItemList {
diff --git a/crates/syntax/src/ast/make/quote.rs b/crates/syntax/src/ast/make/quote.rs
index 1c66a413f8..300ef25c13 100644
--- a/crates/syntax/src/ast/make/quote.rs
+++ b/crates/syntax/src/ast/make/quote.rs
@@ -28,6 +28,19 @@ macro_rules! quote_impl_ {
};
( @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;