generate_function assist don't render snippet if ret type inferred

This commit is contained in:
Josh Mcguigan 2021-03-06 14:29:45 -08:00
parent d645b81b28
commit b275e60905

View file

@ -83,17 +83,18 @@ struct FunctionTemplate {
leading_ws: String, leading_ws: String,
fn_def: ast::Fn, fn_def: ast::Fn,
ret_type: ast::RetType, ret_type: ast::RetType,
should_render_snippet: bool,
trailing_ws: String, trailing_ws: String,
file: FileId, file: FileId,
} }
impl FunctionTemplate { impl FunctionTemplate {
fn to_string(&self, cap: Option<SnippetCap>) -> String { fn to_string(&self, cap: Option<SnippetCap>) -> String {
let f = match cap { let f = match (cap, self.should_render_snippet) {
Some(cap) => { (Some(cap), true) => {
render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax()))
} }
None => self.fn_def.to_string(), _ => self.fn_def.to_string(),
}; };
format!("{}{}{}", self.leading_ws, f, self.trailing_ws) format!("{}{}{}", self.leading_ws, f, self.trailing_ws)
} }
@ -104,7 +105,8 @@ struct FunctionBuilder {
fn_name: ast::Name, fn_name: ast::Name,
type_params: Option<ast::GenericParamList>, type_params: Option<ast::GenericParamList>,
params: ast::ParamList, params: ast::ParamList,
ret_type: Option<ast::RetType>, ret_type: ast::RetType,
should_render_snippet: bool,
file: FileId, file: FileId,
needs_pub: bool, needs_pub: bool,
} }
@ -132,9 +134,44 @@ impl FunctionBuilder {
let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?;
let fn_name = fn_name(&path)?; let fn_name = fn_name(&path)?;
let (type_params, params) = fn_args(ctx, target_module, &call)?; let (type_params, params) = fn_args(ctx, target_module, &call)?;
let ret_type = fn_ret_type(ctx, target_module, &call);
Some(Self { target, fn_name, type_params, params, ret_type, file, needs_pub }) // should_render_snippet intends to express a rough level of confidence about
// the correctness of the return type.
//
// If we are able to infer some return type, and that return type is not unit, we
// don't want to render the snippet. The assumption here is in this situation the
// return type is just as likely to be correct as any other part of the generated
// function.
//
// In the case where the return type is inferred as unit it is likely that the
// user does in fact intend for this generated function to return some non unit
// type, but that the current state of their code doesn't allow that return type
// to be accurately inferred.
let (ret_ty, should_render_snippet) = {
match ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone())) {
Some(ty) if ty.is_unknown() || ty.is_unit() => (make::ty_unit(), true),
Some(ty) => {
let rendered = ty.display_source_code(ctx.db(), target_module.into());
match rendered {
Ok(rendered) => (make::ty(&rendered), false),
Err(_) => (make::ty_unit(), true),
}
}
None => (make::ty_unit(), true),
}
};
let ret_type = make::ret_type(ret_ty);
Some(Self {
target,
fn_name,
type_params,
params,
ret_type,
should_render_snippet,
file,
needs_pub,
})
} }
fn render(self) -> FunctionTemplate { fn render(self) -> FunctionTemplate {
@ -147,7 +184,7 @@ impl FunctionBuilder {
self.type_params, self.type_params,
self.params, self.params,
fn_body, fn_body,
Some(self.ret_type.unwrap_or_else(|| make::ret_type(make::ty_unit()))), Some(self.ret_type),
); );
let leading_ws; let leading_ws;
let trailing_ws; let trailing_ws;
@ -173,6 +210,7 @@ impl FunctionBuilder {
insert_offset, insert_offset,
leading_ws, leading_ws,
ret_type: fn_def.ret_type().unwrap(), ret_type: fn_def.ret_type().unwrap(),
should_render_snippet: self.should_render_snippet,
fn_def, fn_def,
trailing_ws, trailing_ws,
file: self.file, file: self.file,
@ -225,23 +263,6 @@ fn fn_args(
Some((None, make::param_list(None, params))) Some((None, make::param_list(None, params)))
} }
fn fn_ret_type(
ctx: &AssistContext,
target_module: hir::Module,
call: &ast::CallExpr,
) -> Option<ast::RetType> {
let ty = ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone()))?;
if ty.is_unknown() {
return None;
}
if let Ok(rendered) = ty.display_source_code(ctx.db(), target_module.into()) {
Some(make::ret_type(make::ty(&rendered)))
} else {
None
}
}
/// Makes duplicate argument names unique by appending incrementing numbers. /// Makes duplicate argument names unique by appending incrementing numbers.
/// ///
/// ``` /// ```
@ -565,7 +586,7 @@ impl Baz {
} }
} }
fn bar(baz: Baz) ${0:-> Baz} { fn bar(baz: Baz) -> Baz {
todo!() todo!()
} }
", ",
@ -1092,7 +1113,7 @@ fn main() {
let x: u32 = foo(); let x: u32 = foo();
} }
fn foo() ${0:-> u32} { fn foo() -> u32 {
todo!() todo!()
} }
", ",