From 51ec2ced69901f34b3097a6af12b7dd102ed504c Mon Sep 17 00:00:00 2001 From: alibektas Date: Mon, 17 Apr 2023 00:41:08 +0300 Subject: [PATCH] Improve ast::make Add `ty_alias` and make `impl_trait` , `fn` and `impl_` have more coverage. --- .../src/handlers/generate_delegate_methods.rs | 31 +++- .../src/handlers/generate_function.rs | 2 + crates/syntax/src/ast/make.rs | 139 +++++++++++++----- 3 files changed, 132 insertions(+), 40 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index a0813c91154..23e6adcf53a 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -116,12 +116,25 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' ); let ret_type = method_source.ret_type(); let is_async = method_source.async_token().is_some(); + let is_const = method_source.const_token().is_some(); + let is_unsafe = method_source.unsafe_token().is_some(); let tail_expr_finished = if is_async { make::expr_await(tail_expr) } else { tail_expr }; let body = make::block_expr([], Some(tail_expr_finished)); - let f = make::fn_(vis, name, type_params, None, params, body, ret_type, is_async) - .indent(ast::edit::IndentLevel(1)) - .clone_for_update(); + let f = make::fn_( + vis, + name, + type_params, + None, + params, + body, + ret_type, + is_async, + is_const, + is_unsafe, + ) + .indent(ast::edit::IndentLevel(1)) + .clone_for_update(); let cursor = Cursor::Before(f.syntax()); @@ -153,8 +166,16 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let name = &strukt_name.to_string(); let params = strukt.generic_param_list(); let ty_params = params.clone(); - let impl_def = make::impl_(make::ext::ident_path(name), params, ty_params) - .clone_for_update(); + let where_clause = strukt.where_clause(); + + let impl_def = make::impl_( + ty_params, + None, + make::ty_path(make::ext::ident_path(name)), + where_clause, + None, + ) + .clone_for_update(); let assoc_items = impl_def.get_or_create_assoc_item_list(); assoc_items.add_item(f.clone().into()); diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 739b466b311..68b61fc03ff 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -378,6 +378,8 @@ fn render(self, is_method: bool) -> FunctionTemplate { fn_body, self.ret_type, self.is_async, + false, // FIXME : const and unsafe are not handled yet. + false, ); let leading_ws; let trailing_ws; diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index cc0d7f4b48e..3472a424587 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -158,13 +158,6 @@ fn ty_from_text(text: &str) -> ast::Type { ast_from_text(&format!("type _T = {text};")) } -/// Related goto [link](https://doc.rust-lang.org/reference/items/type-aliases.html) -/// Type Alias syntax is -/// ``` -/// TypeAlias : -/// type IDENTIFIER GenericParams? ( : TypeParamBounds )? WhereClause? ( = Type WhereClause?)? ; -/// ``` -/// FIXME : ident should be of type ast::Ident pub fn ty_alias( ident: &str, generic_param_list: Option, @@ -173,7 +166,7 @@ pub fn ty_alias( assignment: Option<(ast::Type, Option)>, ) -> ast::TypeAlias { let mut s = String::new(); - s.push_str(&format!("type {}", ident)); + s.push_str(&format!("type {} ", ident)); if let Some(list) = generic_param_list { s.push_str(&list.to_string()); @@ -203,33 +196,106 @@ pub fn assoc_item_list() -> ast::AssocItemList { ast_from_text("impl C for D {}") } -// FIXME: `ty_params` should be `ast::GenericArgList` -pub fn impl_( - ty: ast::Path, - params: Option, - ty_params: Option, -) -> ast::Impl { - let params = match params { - Some(params) => params.to_string(), - None => String::new(), - }; - let ty_params = match ty_params { - Some(params) => params.to_string(), - None => String::new(), - }; - ast_from_text(&format!("impl{params} {ty}{ty_params} {{}}")) +fn merge_gen_params( + ps: Option, + bs: Option, +) -> Option { + match (ps, bs) { + (None, None) => None, + (None, Some(bs)) => Some(bs), + (Some(ps), None) => Some(ps), + (Some(ps), Some(bs)) => { + for b in bs.generic_params() { + ps.add_generic_param(b); + } + Some(ps) + } + } } -pub fn impl_trait( - trait_: ast::Path, - ty: ast::Path, - ty_params: Option, +pub fn impl_( + generic_params: Option, + generic_args: Option, + path_type: ast::Type, + where_clause: Option, + body: Option>>, ) -> ast::Impl { - // TODO : If this function is now correct we can also change `impl_` accordingly` - let ty_params_str = ty_params.as_ref().map_or_else(String::new, |params| params.to_string()); - let ty_genargs_str = - ty_params.map_or_else(String::new, |params| params.to_generic_args().to_string()); - ast_from_text(&format!("impl{ty_params_str} {trait_} for {ty}{ty_genargs_str} {{}}")) + let (gen_params, tr_gen_args) = match (generic_params, generic_args) { + (None, None) => (String::new(), String::new()), + (None, Some(args)) => (String::new(), args.to_generic_args().to_string()), + (Some(params), None) => (params.to_string(), params.to_generic_args().to_string()), + (Some(params), Some(args)) => match merge_gen_params(Some(params.clone()), Some(args)) { + Some(merged) => (params.to_string(), merged.to_generic_args().to_string()), + None => (params.to_string(), String::new()), + }, + }; + + let where_clause = match where_clause { + Some(pr) => pr.to_string(), + None => " ".to_string(), + }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), + None => String::new(), + }; + + ast_from_text(&format!("impl{gen_params} {path_type}{tr_gen_args}{where_clause}{{{}}}", body)) +} + +// FIXME : We must make *_gen_args' type ast::GenericArgList but in order to do so we must implement in `edit_in_place.rs` +// `add_generic_arg()` just like `add_generic_param()` +// is implemented for `ast::GenericParamList` +pub fn impl_trait( + is_unsafe: bool, + trait_gen_params: Option, + trait_gen_args: Option, + type_gen_params: Option, + type_gen_args: Option, + is_negative: bool, + path_type: ast::Type, + ty: ast::Type, + trait_where_clause: Option, + ty_where_clause: Option, + body: Option>>, +) -> ast::Impl { + let is_unsafe = if is_unsafe { "unsafe " } else { "" }; + let ty_gen_args = match merge_gen_params(type_gen_params.clone(), type_gen_args) { + Some(pars) => pars.to_generic_args().to_string(), + None => String::new(), + }; + + let tr_gen_args = match merge_gen_params(trait_gen_params.clone(), trait_gen_args) { + Some(pars) => pars.to_generic_args().to_string(), + None => String::new(), + }; + + let gen_params = match merge_gen_params(trait_gen_params, type_gen_params) { + Some(pars) => pars.to_string(), + None => String::new(), + }; + + let is_negative = if is_negative { "! " } else { "" }; + + let where_clause = match (ty_where_clause, trait_where_clause) { + (None, None) => " ".to_string(), + (None, Some(tr)) => format!("\n{}\n", tr).to_string(), + (Some(ty), None) => format!("\n{}\n", ty).to_string(), + (Some(ty), Some(tr)) => { + let updated = ty.clone_for_update(); + tr.predicates().for_each(|p| { + ty.add_predicate(p); + }); + format!("\n{}\n", updated).to_string() + } + }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), + None => String::new(), + }; + + ast_from_text(&format!("{is_unsafe}impl{gen_params} {is_negative}{path_type}{tr_gen_args} for {ty}{ty_gen_args}{where_clause}{{{}}}" , body)) } pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { @@ -871,6 +937,8 @@ pub fn fn_( body: ast::BlockExpr, ret_type: Option, is_async: bool, + is_const: bool, + is_unsafe: bool, ) -> ast::Fn { let type_params = match type_params { Some(type_params) => format!("{type_params}"), @@ -890,12 +958,13 @@ pub fn fn_( }; let async_literal = if is_async { "async " } else { "" }; + let const_literal = if is_const { "const " } else { "" }; + let unsafe_literal = if is_unsafe { "unsafe " } else { "" }; ast_from_text(&format!( - "{visibility}{async_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", + "{visibility}{async_literal}{const_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", )) } - pub fn struct_( visibility: Option, strukt_name: ast::Name, @@ -945,7 +1014,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: Lazy> = Lazy::new(|| { SourceFile::parse( - "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p)\n;\n\n", + "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n", ) });