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, assignment: Option<(ast::Type, Option)>, ) -> 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>( - for_lifetime_list: Option, is_unsafe: bool, abi: Option, - params: I, + mut params: I, ret_type: Option, ) -> 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 { 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 @@ -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 ToNodeChild for N { fn append_node_child(self, children: &mut Vec>) { - 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 ToNodeChild for Option { } } +// 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>) {} +} + pub(crate) const fn verify_only_whitespaces(text: &str) { let text = text.as_bytes(); let mut i = 0;