Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/syntax/src/ast/make.rs')
| -rw-r--r-- | crates/syntax/src/ast/make.rs | 126 |
1 files changed, 65 insertions, 61 deletions
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index eb96ab6ef5..282cbc4b3a 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -8,7 +8,10 @@ //! 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; use itertools::Itertools; use parser::{Edition, T}; @@ -16,7 +19,7 @@ use rowan::NodeOrToken; use stdx::{format_to, format_to_acc, never}; use crate::{ - ast::{self, Param}, + ast::{self, make::quote::quote, Param}, utils::is_raw_identifier, AstNode, SourceFile, SyntaxKind, SyntaxToken, }; @@ -118,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) { @@ -135,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 @@ -175,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 { @@ -351,6 +336,24 @@ pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { ast_from_text(&format!("type __ = {name_ref};")) } +/// Type and expressions/patterns path differ in whether they require `::` before generic arguments. +/// Type paths allow them but they are often omitted, while expression/pattern paths require them. +pub fn generic_ty_path_segment( + name_ref: ast::NameRef, + generic_args: impl IntoIterator<Item = ast::GenericArg>, +) -> ast::PathSegment { + let mut generic_args = generic_args.into_iter(); + let first_generic_arg = generic_args.next(); + quote! { + PathSegment { + #name_ref + GenericArgList { + [<] #first_generic_arg #([,] " " #generic_args)* [>] + } + } + } +} + pub fn path_segment_ty(type_ref: ast::Type, trait_ref: Option<ast::PathType>) -> ast::PathSegment { let text = match trait_ref { Some(trait_ref) => format!("fn f(x: <{type_ref} as {trait_ref}>) {{}}"), @@ -480,15 +483,16 @@ pub fn block_expr( stmts: impl IntoIterator<Item = ast::Stmt>, tail_expr: Option<ast::Expr>, ) -> ast::BlockExpr { - let mut buf = "{\n".to_owned(); - for stmt in stmts.into_iter() { - format_to!(buf, " {stmt}\n"); - } - if let Some(tail_expr) = tail_expr { - format_to!(buf, " {tail_expr}\n"); + quote! { + BlockExpr { + StmtList { + ['{'] "\n" + #(" " #stmts "\n")* + #(" " #tail_expr "\n")* + ['}'] + } + } } - buf += "}"; - ast_from_text(&format!("fn f() {buf}")) } pub fn async_move_block_expr( @@ -815,7 +819,7 @@ pub fn match_arm_with_guard( pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList { let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| { - let needs_comma = arm.expr().map_or(true, |it| !it.is_block_like()); + let needs_comma = arm.expr().is_none_or(|it| !it.is_block_like()); let comma = if needs_comma { "," } else { "" }; let arm = arm.syntax(); format_to_acc!(acc, " {arm}{comma}\n") @@ -828,7 +832,7 @@ pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::Mat } pub fn where_pred( - path: ast::Path, + path: ast::Type, bounds: impl IntoIterator<Item = ast::TypeBound>, ) -> ast::WherePred { let bounds = bounds.into_iter().join(" + "); |