mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 07:44:10 +00:00
move trait body gen code to utils
This commit is contained in:
parent
8e4fb4f518
commit
4d2e25a034
@ -2,13 +2,12 @@ use hir::ModuleDef;
|
||||
use ide_db::helpers::{import_assets::NameToImport, mod_path_to_ast};
|
||||
use ide_db::items_locator;
|
||||
use itertools::Itertools;
|
||||
use syntax::ast::edit::AstNodeEdit;
|
||||
use syntax::ted;
|
||||
use syntax::{
|
||||
ast::{self, make, AstNode, NameOwner},
|
||||
SyntaxKind::{IDENT, WHITESPACE},
|
||||
};
|
||||
|
||||
use crate::utils::gen_trait_body;
|
||||
use crate::{
|
||||
assist_context::{AssistBuilder, AssistContext, Assists},
|
||||
utils::{
|
||||
@ -169,158 +168,12 @@ fn impl_def_from_trait(
|
||||
|
||||
// Generate a default `impl` function body for the derived trait.
|
||||
if let ast::AssocItem::Fn(ref func) = first_assoc_item {
|
||||
let _ = gen_trait_body_impl(func, trait_path, adt, annotated_name);
|
||||
let _ = gen_trait_body(func, trait_path, adt, annotated_name);
|
||||
};
|
||||
|
||||
Some((impl_def, first_assoc_item))
|
||||
}
|
||||
|
||||
/// Generate custom trait bodies where possible.
|
||||
///
|
||||
/// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
|
||||
/// `None` means that generating a custom trait body failed, and the body will remain
|
||||
/// as `todo!` instead.
|
||||
fn gen_trait_body_impl(
|
||||
func: &ast::Fn,
|
||||
trait_path: &ast::Path,
|
||||
adt: &ast::Adt,
|
||||
annotated_name: &ast::Name,
|
||||
) -> Option<()> {
|
||||
match trait_path.segment()?.name_ref()?.text().as_str() {
|
||||
"Debug" => gen_debug_impl(adt, func, annotated_name),
|
||||
"Default" => gen_default_impl(adt, func),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a `Debug` impl based on the fields and members of the target type.
|
||||
fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) -> Option<()> {
|
||||
match adt {
|
||||
// `Debug` cannot be derived for unions, so no default impl can be provided.
|
||||
ast::Adt::Union(_) => None,
|
||||
|
||||
// => match self { Self::Variant => write!(f, "Variant") }
|
||||
ast::Adt::Enum(enum_) => {
|
||||
let list = enum_.variant_list()?;
|
||||
let mut arms = vec![];
|
||||
for variant in list.variants() {
|
||||
let name = variant.name()?;
|
||||
let left = make::ext::ident_path("Self");
|
||||
let right = make::ext::ident_path(&format!("{}", name));
|
||||
let variant_name = make::path_pat(make::path_concat(left, right));
|
||||
|
||||
let target = make::expr_path(make::ext::ident_path("f").into());
|
||||
let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into();
|
||||
let args = make::arg_list(vec![target, fmt_string]);
|
||||
let macro_name = make::expr_path(make::ext::ident_path("write"));
|
||||
let macro_call = make::expr_macro_call(macro_name, args);
|
||||
|
||||
arms.push(make::match_arm(Some(variant_name.into()), None, macro_call.into()));
|
||||
}
|
||||
|
||||
let match_target = make::expr_path(make::ext::ident_path("self"));
|
||||
let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
|
||||
let match_expr = make::expr_match(match_target, list);
|
||||
|
||||
let body = make::block_expr(None, Some(match_expr));
|
||||
let body = body.indent(ast::edit::IndentLevel(1));
|
||||
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
|
||||
Some(())
|
||||
}
|
||||
|
||||
ast::Adt::Struct(strukt) => {
|
||||
let name = format!("\"{}\"", annotated_name);
|
||||
let args = make::arg_list(Some(make::expr_literal(&name).into()));
|
||||
let target = make::expr_path(make::ext::ident_path("f"));
|
||||
|
||||
let expr = match strukt.field_list() {
|
||||
// => f.debug_struct("Name").finish()
|
||||
None => make::expr_method_call(target, make::name_ref("debug_struct"), args),
|
||||
|
||||
// => f.debug_struct("Name").field("foo", &self.foo).finish()
|
||||
Some(ast::FieldList::RecordFieldList(field_list)) => {
|
||||
let method = make::name_ref("debug_struct");
|
||||
let mut expr = make::expr_method_call(target, method, args);
|
||||
for field in field_list.fields() {
|
||||
let name = field.name()?;
|
||||
let f_name = make::expr_literal(&(format!("\"{}\"", name))).into();
|
||||
let f_path = make::expr_path(make::ext::ident_path("self"));
|
||||
let f_path = make::expr_ref(f_path, false);
|
||||
let f_path = make::expr_field(f_path, &format!("{}", name)).into();
|
||||
let args = make::arg_list(vec![f_name, f_path]);
|
||||
expr = make::expr_method_call(expr, make::name_ref("field"), args);
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
// => f.debug_tuple("Name").field(self.0).finish()
|
||||
Some(ast::FieldList::TupleFieldList(field_list)) => {
|
||||
let method = make::name_ref("debug_tuple");
|
||||
let mut expr = make::expr_method_call(target, method, args);
|
||||
for (idx, _) in field_list.fields().enumerate() {
|
||||
let f_path = make::expr_path(make::ext::ident_path("self"));
|
||||
let f_path = make::expr_ref(f_path, false);
|
||||
let f_path = make::expr_field(f_path, &format!("{}", idx)).into();
|
||||
let method = make::name_ref("field");
|
||||
expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)));
|
||||
}
|
||||
expr
|
||||
}
|
||||
};
|
||||
|
||||
let method = make::name_ref("finish");
|
||||
let expr = make::expr_method_call(expr, method, make::arg_list(None));
|
||||
let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
|
||||
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a `Debug` impl based on the fields and members of the target type.
|
||||
fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
||||
fn gen_default_call() -> ast::Expr {
|
||||
let trait_name = make::ext::ident_path("Default");
|
||||
let method_name = make::ext::ident_path("default");
|
||||
let fn_name = make::expr_path(make::path_concat(trait_name, method_name));
|
||||
make::expr_call(fn_name, make::arg_list(None))
|
||||
}
|
||||
match adt {
|
||||
// `Debug` cannot be derived for unions, so no default impl can be provided.
|
||||
ast::Adt::Union(_) => None,
|
||||
// Deriving `Debug` for enums is not stable yet.
|
||||
ast::Adt::Enum(_) => None,
|
||||
ast::Adt::Struct(strukt) => {
|
||||
let expr = match strukt.field_list() {
|
||||
Some(ast::FieldList::RecordFieldList(field_list)) => {
|
||||
let mut fields = vec![];
|
||||
for field in field_list.fields() {
|
||||
let method_call = gen_default_call();
|
||||
let name_ref = make::name_ref(&field.name()?.to_string());
|
||||
let field = make::record_expr_field(name_ref, Some(method_call));
|
||||
fields.push(field);
|
||||
}
|
||||
let struct_name = make::ext::ident_path("Self");
|
||||
let fields = make::record_expr_field_list(fields);
|
||||
make::record_expr(struct_name, fields).into()
|
||||
}
|
||||
Some(ast::FieldList::TupleFieldList(field_list)) => {
|
||||
let struct_name = make::expr_path(make::ext::ident_path("Self"));
|
||||
let fields = field_list.fields().map(|_| gen_default_call());
|
||||
make::expr_call(struct_name, make::arg_list(fields))
|
||||
}
|
||||
None => {
|
||||
let struct_name = make::ext::ident_path("Self");
|
||||
let fields = make::record_expr_field_list(None);
|
||||
make::record_expr(struct_name, fields).into()
|
||||
}
|
||||
};
|
||||
let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
|
||||
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn update_attribute(
|
||||
builder: &mut AssistBuilder,
|
||||
input: &ast::TokenTree,
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Assorted functions shared by several assists.
|
||||
|
||||
pub(crate) mod suggest_name;
|
||||
mod gen_trait_body;
|
||||
|
||||
use std::ops;
|
||||
|
||||
@ -25,6 +26,8 @@ use syntax::{
|
||||
|
||||
use crate::assist_context::{AssistBuilder, AssistContext};
|
||||
|
||||
pub(crate) use gen_trait_body::gen_trait_body;
|
||||
|
||||
pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
|
||||
extract_trivial_expression(&block)
|
||||
.filter(|expr| !expr.syntax().text().contains_char('\n'))
|
||||
|
149
crates/ide_assists/src/utils/gen_trait_body.rs
Normal file
149
crates/ide_assists/src/utils/gen_trait_body.rs
Normal file
@ -0,0 +1,149 @@
|
||||
use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, NameOwner};
|
||||
use syntax::ted;
|
||||
|
||||
/// Generate custom trait bodies where possible.
|
||||
///
|
||||
/// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
|
||||
/// `None` means that generating a custom trait body failed, and the body will remain
|
||||
/// as `todo!` instead.
|
||||
pub(crate) fn gen_trait_body(
|
||||
func: &ast::Fn,
|
||||
trait_path: &ast::Path,
|
||||
adt: &ast::Adt,
|
||||
annotated_name: &ast::Name,
|
||||
) -> Option<()> {
|
||||
match trait_path.segment()?.name_ref()?.text().as_str() {
|
||||
"Debug" => gen_debug_impl(adt, func, annotated_name),
|
||||
"Default" => gen_default_impl(adt, func),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a `Debug` impl based on the fields and members of the target type.
|
||||
fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) -> Option<()> {
|
||||
match adt {
|
||||
// `Debug` cannot be derived for unions, so no default impl can be provided.
|
||||
ast::Adt::Union(_) => None,
|
||||
|
||||
// => match self { Self::Variant => write!(f, "Variant") }
|
||||
ast::Adt::Enum(enum_) => {
|
||||
let list = enum_.variant_list()?;
|
||||
let mut arms = vec![];
|
||||
for variant in list.variants() {
|
||||
let name = variant.name()?;
|
||||
let left = make::ext::ident_path("Self");
|
||||
let right = make::ext::ident_path(&format!("{}", name));
|
||||
let variant_name = make::path_pat(make::path_concat(left, right));
|
||||
|
||||
let target = make::expr_path(make::ext::ident_path("f").into());
|
||||
let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into();
|
||||
let args = make::arg_list(vec![target, fmt_string]);
|
||||
let macro_name = make::expr_path(make::ext::ident_path("write"));
|
||||
let macro_call = make::expr_macro_call(macro_name, args);
|
||||
|
||||
arms.push(make::match_arm(Some(variant_name.into()), None, macro_call.into()));
|
||||
}
|
||||
|
||||
let match_target = make::expr_path(make::ext::ident_path("self"));
|
||||
let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
|
||||
let match_expr = make::expr_match(match_target, list);
|
||||
|
||||
let body = make::block_expr(None, Some(match_expr));
|
||||
let body = body.indent(ast::edit::IndentLevel(1));
|
||||
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
|
||||
Some(())
|
||||
}
|
||||
|
||||
ast::Adt::Struct(strukt) => {
|
||||
let name = format!("\"{}\"", annotated_name);
|
||||
let args = make::arg_list(Some(make::expr_literal(&name).into()));
|
||||
let target = make::expr_path(make::ext::ident_path("f"));
|
||||
|
||||
let expr = match strukt.field_list() {
|
||||
// => f.debug_struct("Name").finish()
|
||||
None => make::expr_method_call(target, make::name_ref("debug_struct"), args),
|
||||
|
||||
// => f.debug_struct("Name").field("foo", &self.foo).finish()
|
||||
Some(ast::FieldList::RecordFieldList(field_list)) => {
|
||||
let method = make::name_ref("debug_struct");
|
||||
let mut expr = make::expr_method_call(target, method, args);
|
||||
for field in field_list.fields() {
|
||||
let name = field.name()?;
|
||||
let f_name = make::expr_literal(&(format!("\"{}\"", name))).into();
|
||||
let f_path = make::expr_path(make::ext::ident_path("self"));
|
||||
let f_path = make::expr_ref(f_path, false);
|
||||
let f_path = make::expr_field(f_path, &format!("{}", name)).into();
|
||||
let args = make::arg_list(vec![f_name, f_path]);
|
||||
expr = make::expr_method_call(expr, make::name_ref("field"), args);
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
// => f.debug_tuple("Name").field(self.0).finish()
|
||||
Some(ast::FieldList::TupleFieldList(field_list)) => {
|
||||
let method = make::name_ref("debug_tuple");
|
||||
let mut expr = make::expr_method_call(target, method, args);
|
||||
for (idx, _) in field_list.fields().enumerate() {
|
||||
let f_path = make::expr_path(make::ext::ident_path("self"));
|
||||
let f_path = make::expr_ref(f_path, false);
|
||||
let f_path = make::expr_field(f_path, &format!("{}", idx)).into();
|
||||
let method = make::name_ref("field");
|
||||
expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)));
|
||||
}
|
||||
expr
|
||||
}
|
||||
};
|
||||
|
||||
let method = make::name_ref("finish");
|
||||
let expr = make::expr_method_call(expr, method, make::arg_list(None));
|
||||
let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
|
||||
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a `Debug` impl based on the fields and members of the target type.
|
||||
fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
||||
fn gen_default_call() -> ast::Expr {
|
||||
let trait_name = make::ext::ident_path("Default");
|
||||
let method_name = make::ext::ident_path("default");
|
||||
let fn_name = make::expr_path(make::path_concat(trait_name, method_name));
|
||||
make::expr_call(fn_name, make::arg_list(None))
|
||||
}
|
||||
match adt {
|
||||
// `Debug` cannot be derived for unions, so no default impl can be provided.
|
||||
ast::Adt::Union(_) => None,
|
||||
// Deriving `Debug` for enums is not stable yet.
|
||||
ast::Adt::Enum(_) => None,
|
||||
ast::Adt::Struct(strukt) => {
|
||||
let expr = match strukt.field_list() {
|
||||
Some(ast::FieldList::RecordFieldList(field_list)) => {
|
||||
let mut fields = vec![];
|
||||
for field in field_list.fields() {
|
||||
let method_call = gen_default_call();
|
||||
let name_ref = make::name_ref(&field.name()?.to_string());
|
||||
let field = make::record_expr_field(name_ref, Some(method_call));
|
||||
fields.push(field);
|
||||
}
|
||||
let struct_name = make::ext::ident_path("Self");
|
||||
let fields = make::record_expr_field_list(fields);
|
||||
make::record_expr(struct_name, fields).into()
|
||||
}
|
||||
Some(ast::FieldList::TupleFieldList(field_list)) => {
|
||||
let struct_name = make::expr_path(make::ext::ident_path("Self"));
|
||||
let fields = field_list.fields().map(|_| gen_default_call());
|
||||
make::expr_call(struct_name, make::arg_list(fields))
|
||||
}
|
||||
None => {
|
||||
let struct_name = make::ext::ident_path("Self");
|
||||
let fields = make::record_expr_field_list(None);
|
||||
make::record_expr(struct_name, fields).into()
|
||||
}
|
||||
};
|
||||
let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
|
||||
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user