Skip to content

Commit 25e37e2

Browse files
Merge #4175
4175: Introduce HirDisplay method for rendering source code & use it in add_function assist r=flodiebold a=TimoFreiberg Next feature for #3639. So far the only change in the new `HirDisplay` method is that paths are qualified, but more changes will be necessary (omitting the function name from function types, returning an error instead of printing `"{unknown}"`, probably more). Is that approach okay? Co-authored-by: Timo Freiberg <[email protected]>
2 parents d7a0b0f + 64e6b82 commit 25e37e2

File tree

5 files changed

+300
-99
lines changed

5 files changed

+300
-99
lines changed

crates/ra_assists/src/handlers/add_function.rs

+116-58
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,12 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
4343
return None;
4444
}
4545

46-
let target_module = if let Some(qualifier) = path.qualifier() {
47-
if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) =
48-
ctx.sema.resolve_path(&qualifier)
49-
{
50-
Some(module.definition_source(ctx.sema.db))
51-
} else {
52-
return None;
53-
}
54-
} else {
55-
None
46+
let target_module = match path.qualifier() {
47+
Some(qualifier) => match ctx.sema.resolve_path(&qualifier) {
48+
Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => Some(module),
49+
_ => return None,
50+
},
51+
None => None,
5652
};
5753

5854
let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
@@ -83,25 +79,29 @@ struct FunctionBuilder {
8379
}
8480

8581
impl FunctionBuilder {
86-
/// Prepares a generated function that matches `call` in `generate_in`
87-
/// (or as close to `call` as possible, if `generate_in` is `None`)
82+
/// Prepares a generated function that matches `call`.
83+
/// The function is generated in `target_module` or next to `call`
8884
fn from_call(
8985
ctx: &AssistContext,
9086
call: &ast::CallExpr,
9187
path: &ast::Path,
92-
target_module: Option<hir::InFile<hir::ModuleSource>>,
88+
target_module: Option<hir::Module>,
9389
) -> Option<Self> {
94-
let needs_pub = target_module.is_some();
9590
let mut file = ctx.frange.file_id;
96-
let target = if let Some(target_module) = target_module {
97-
let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?;
98-
file = in_file;
99-
target
100-
} else {
101-
next_space_for_fn_after_call_site(&call)?
91+
let target = match &target_module {
92+
Some(target_module) => {
93+
let module_source = target_module.definition_source(ctx.db);
94+
let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?;
95+
file = in_file;
96+
target
97+
}
98+
None => next_space_for_fn_after_call_site(&call)?,
10299
};
100+
let needs_pub = target_module.is_some();
101+
let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?;
103102
let fn_name = fn_name(&path)?;
104-
let (type_params, params) = fn_args(ctx, &call)?;
103+
let (type_params, params) = fn_args(ctx, target_module, &call)?;
104+
105105
Some(Self { target, fn_name, type_params, params, file, needs_pub })
106106
}
107107

@@ -144,6 +144,15 @@ enum GeneratedFunctionTarget {
144144
InEmptyItemList(ast::ItemList),
145145
}
146146

147+
impl GeneratedFunctionTarget {
148+
fn syntax(&self) -> &SyntaxNode {
149+
match self {
150+
GeneratedFunctionTarget::BehindItem(it) => it,
151+
GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(),
152+
}
153+
}
154+
}
155+
147156
fn fn_name(call: &ast::Path) -> Option<ast::Name> {
148157
let name = call.segment()?.syntax().to_string();
149158
Some(ast::make::name(&name))
@@ -152,17 +161,17 @@ fn fn_name(call: &ast::Path) -> Option<ast::Name> {
152161
/// Computes the type variables and arguments required for the generated function
153162
fn fn_args(
154163
ctx: &AssistContext,
164+
target_module: hir::Module,
155165
call: &ast::CallExpr,
156166
) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> {
157167
let mut arg_names = Vec::new();
158168
let mut arg_types = Vec::new();
159169
for arg in call.arg_list()?.args() {
160-
let arg_name = match fn_arg_name(&arg) {
170+
arg_names.push(match fn_arg_name(&arg) {
161171
Some(name) => name,
162172
None => String::from("arg"),
163-
};
164-
arg_names.push(arg_name);
165-
arg_types.push(match fn_arg_type(ctx, &arg) {
173+
});
174+
arg_types.push(match fn_arg_type(ctx, target_module, &arg) {
166175
Some(ty) => ty,
167176
None => String::from("()"),
168177
});
@@ -218,12 +227,21 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
218227
}
219228
}
220229

221-
fn fn_arg_type(ctx: &AssistContext, fn_arg: &ast::Expr) -> Option<String> {
230+
fn fn_arg_type(
231+
ctx: &AssistContext,
232+
target_module: hir::Module,
233+
fn_arg: &ast::Expr,
234+
) -> Option<String> {
222235
let ty = ctx.sema.type_of_expr(fn_arg)?;
223236
if ty.is_unknown() {
224237
return None;
225238
}
226-
Some(ty.display(ctx.sema.db).to_string())
239+
240+
if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) {
241+
Some(rendered)
242+
} else {
243+
None
244+
}
227245
}
228246

229247
/// Returns the position inside the current mod or file
@@ -252,10 +270,10 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFu
252270

253271
fn next_space_for_fn_in_module(
254272
db: &dyn hir::db::AstDatabase,
255-
module: hir::InFile<hir::ModuleSource>,
273+
module_source: &hir::InFile<hir::ModuleSource>,
256274
) -> Option<(FileId, GeneratedFunctionTarget)> {
257-
let file = module.file_id.original_file(db);
258-
let assist_item = match module.value {
275+
let file = module_source.file_id.original_file(db);
276+
let assist_item = match &module_source.value {
259277
hir::ModuleSource::SourceFile(it) => {
260278
if let Some(last_item) = it.items().last() {
261279
GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())
@@ -599,8 +617,33 @@ fn bar(foo: impl Foo) {
599617
}
600618

601619
#[test]
602-
#[ignore]
603-
// FIXME print paths properly to make this test pass
620+
fn borrowed_arg() {
621+
check_assist(
622+
add_function,
623+
r"
624+
struct Baz;
625+
fn baz() -> Baz { todo!() }
626+
627+
fn foo() {
628+
bar<|>(&baz())
629+
}
630+
",
631+
r"
632+
struct Baz;
633+
fn baz() -> Baz { todo!() }
634+
635+
fn foo() {
636+
bar(&baz())
637+
}
638+
639+
fn bar(baz: &Baz) {
640+
<|>todo!()
641+
}
642+
",
643+
)
644+
}
645+
646+
#[test]
604647
fn add_function_with_qualified_path_arg() {
605648
check_assist(
606649
add_function,
@@ -609,25 +652,21 @@ mod Baz {
609652
pub struct Bof;
610653
pub fn baz() -> Bof { Bof }
611654
}
612-
mod Foo {
613-
fn foo() {
614-
<|>bar(super::Baz::baz())
615-
}
655+
fn foo() {
656+
<|>bar(Baz::baz())
616657
}
617658
",
618659
r"
619660
mod Baz {
620661
pub struct Bof;
621662
pub fn baz() -> Bof { Bof }
622663
}
623-
mod Foo {
624-
fn foo() {
625-
bar(super::Baz::baz())
626-
}
664+
fn foo() {
665+
bar(Baz::baz())
666+
}
627667
628-
fn bar(baz: super::Baz::Bof) {
629-
<|>todo!()
630-
}
668+
fn bar(baz: Baz::Bof) {
669+
<|>todo!()
631670
}
632671
",
633672
)
@@ -808,6 +847,40 @@ fn foo() {
808847
)
809848
}
810849

850+
#[test]
851+
#[ignore]
852+
// Ignored until local imports are supported.
853+
// See https://github.com/rust-analyzer/rust-analyzer/issues/1165
854+
fn qualified_path_uses_correct_scope() {
855+
check_assist(
856+
add_function,
857+
"
858+
mod foo {
859+
pub struct Foo;
860+
}
861+
fn bar() {
862+
use foo::Foo;
863+
let foo = Foo;
864+
baz<|>(foo)
865+
}
866+
",
867+
"
868+
mod foo {
869+
pub struct Foo;
870+
}
871+
fn bar() {
872+
use foo::Foo;
873+
let foo = Foo;
874+
baz(foo)
875+
}
876+
877+
fn baz(foo: foo::Foo) {
878+
<|>todo!()
879+
}
880+
",
881+
)
882+
}
883+
811884
#[test]
812885
fn add_function_in_module_containing_other_items() {
813886
check_assist(
@@ -919,21 +992,6 @@ fn bar(baz: ()) {}
919992
)
920993
}
921994

922-
#[test]
923-
fn add_function_not_applicable_if_function_path_not_singleton() {
924-
// In the future this assist could be extended to generate functions
925-
// if the path is in the same crate (or even the same workspace).
926-
// For the beginning, I think this is fine.
927-
check_assist_not_applicable(
928-
add_function,
929-
r"
930-
fn foo() {
931-
other_crate::bar<|>();
932-
}
933-
",
934-
)
935-
}
936-
937995
#[test]
938996
#[ignore]
939997
fn create_method_with_no_args() {

crates/ra_hir/src/code_model.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ use hir_expand::{
2222
MacroDefId, MacroDefKind,
2323
};
2424
use hir_ty::{
25-
autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy,
26-
Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor,
25+
autoderef,
26+
display::{HirDisplayError, HirFormatter},
27+
expr::ExprValidator,
28+
method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty,
29+
TyDefId, TypeCtor,
2730
};
2831
use ra_db::{CrateId, CrateName, Edition, FileId};
2932
use ra_prof::profile;
@@ -1319,7 +1322,7 @@ impl Type {
13191322
}
13201323

13211324
impl HirDisplay for Type {
1322-
fn hir_fmt(&self, f: &mut HirFormatter) -> std::fmt::Result {
1325+
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
13231326
self.ty.value.hir_fmt(f)
13241327
}
13251328
}

0 commit comments

Comments
 (0)