diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs index f0dfe778090..2b072686991 100644 --- a/crates/ra_assists/src/assists/add_missing_impl_members.rs +++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs @@ -139,6 +139,12 @@ fn add_missing_impl_members_inner( ctx.add_assist(AssistId(assist_id), label, |edit| { let n_existing_items = impl_item_list.impl_items().count(); + let module = hir::SourceAnalyzer::new( + db, + hir::InFile::new(file_id.into(), impl_node.syntax()), + None, + ) + .module(); let substs = get_syntactic_substs(impl_node).unwrap_or_default(); let generic_def: hir::GenericDef = trait_.into(); let substs_by_param: HashMap<_, _> = generic_def @@ -150,6 +156,10 @@ fn add_missing_impl_members_inner( .collect(); let items = missing_items .into_iter() + .map(|it| match module { + Some(module) => qualify_paths(db, hir::InFile::new(file_id.into(), it), module), + None => it, + }) .map(|it| { substitute_type_params(db, hir::InFile::new(file_id.into(), it), &substs_by_param) }) @@ -227,6 +237,41 @@ fn substitute_type_params( } } +use hir::PathResolution; + +// TODO handle partial paths, with generic args +// TODO handle value ns? + +fn qualify_paths(db: &impl HirDatabase, node: hir::InFile, from: hir::Module) -> N { + let path_replacements = node + .value + .syntax() + .descendants() + .filter_map(ast::Path::cast) + .filter_map(|p| { + let analyzer = hir::SourceAnalyzer::new(db, node.with_value(p.syntax()), None); + let resolution = analyzer.resolve_path(db, &p)?; + match resolution { + PathResolution::Def(def) => { + let found_path = from.find_path(db, def)?; + Some((p, found_path.to_ast())) + } + PathResolution::Local(_) + | PathResolution::TypeParam(_) + | PathResolution::SelfType(_) => None, + PathResolution::Macro(_) => None, + PathResolution::AssocItem(_) => None, + } + }) + .collect::>(); + + if path_replacements.is_empty() { + node.value + } else { + edit::replace_descendants(&node.value, path_replacements.into_iter()) + } +} + /// Given an `ast::ImplBlock`, resolves the target trait (the one being /// implemented) to a `ast::TraitDef`. fn resolve_target_trait_def( @@ -406,14 +451,14 @@ impl Foo for S { add_missing_impl_members, " mod foo { - struct Bar; + pub struct Bar; trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { <|> }", " mod foo { - struct Bar; + pub struct Bar; trait Foo { fn foo(&self, bar: Bar); } } struct S; diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index cc42068a100..4da3db0d6b8 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -227,6 +227,19 @@ impl Module { pub(crate) fn with_module_id(self, module_id: LocalModuleId) -> Module { Module::new(self.krate(), module_id) } + + pub fn find_path( + self, + db: &impl DefDatabase, + item: ModuleDef, + ) -> Option { + // FIXME expose namespace choice + hir_def::find_path::find_path( + db, + hir_def::item_scope::ItemInNs::Types(item.into()), + self.into(), + ) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/crates/ra_hir/src/from_id.rs b/crates/ra_hir/src/from_id.rs index 75a1a777221..c16c17072b7 100644 --- a/crates/ra_hir/src/from_id.rs +++ b/crates/ra_hir/src/from_id.rs @@ -91,6 +91,22 @@ impl From for ModuleDef { } } +impl From for ModuleDefId { + fn from(id: ModuleDef) -> Self { + match id { + ModuleDef::Module(it) => ModuleDefId::ModuleId(it.into()), + ModuleDef::Function(it) => ModuleDefId::FunctionId(it.into()), + ModuleDef::Adt(it) => ModuleDefId::AdtId(it.into()), + ModuleDef::EnumVariant(it) => ModuleDefId::EnumVariantId(it.into()), + ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()), + ModuleDef::Static(it) => ModuleDefId::StaticId(it.into()), + ModuleDef::Trait(it) => ModuleDefId::TraitId(it.into()), + ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()), + ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it), + } + } +} + impl From for DefWithBodyId { fn from(def: DefWithBody) -> Self { match def { diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 2c422af8bd6..71339565f06 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -205,6 +205,10 @@ impl SourceAnalyzer { } } + pub fn module(&self) -> Option { + Some(crate::code_model::Module { id: self.resolver.module_id()? }) + } + fn expr_id(&self, expr: &ast::Expr) -> Option { let src = InFile { file_id: self.file_id, value: expr }; self.body_source_map.as_ref()?.node_expr(src) diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index 82cfa67a9a4..7dd1939b988 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs @@ -1,7 +1,7 @@ //! A desugared representation of paths like `crate::foo` or `::bar`. mod lower; -use std::{iter, sync::Arc}; +use std::{fmt::Display, iter, sync::Arc}; use hir_expand::{ hygiene::Hygiene, @@ -78,6 +78,12 @@ impl ModPath { } self.segments.first() } + + pub fn to_ast(&self) -> ast::Path { + use ast::AstNode; + let parse = ast::SourceFile::parse(&self.to_string()); + parse.tree().syntax().descendants().find_map(ast::Path::cast).unwrap() + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -248,6 +254,42 @@ impl From for ModPath { } } +impl Display for ModPath { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut first_segment = true; + let mut add_segment = |s| { + if !first_segment { + f.write_str("::")?; + } + first_segment = false; + f.write_str(s)?; + Ok(()) + }; + match self.kind { + PathKind::Plain => {} + PathKind::Super(n) => { + if n == 0 { + add_segment("self")?; + } + for _ in 0..n { + add_segment("super")?; + } + } + PathKind::Crate => add_segment("crate")?, + PathKind::Abs => add_segment("")?, + PathKind::DollarCrate(_) => add_segment("$crate")?, + } + for segment in &self.segments { + if !first_segment { + f.write_str("::")?; + } + first_segment = false; + write!(f, "{}", segment)?; + } + Ok(()) + } +} + pub use hir_expand::name as __name; #[macro_export] diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index 5d16dd0871e..40d0cb58808 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs @@ -411,6 +411,11 @@ impl Resolver { }) } + pub fn module_id(&self) -> Option { + let (def_map, local_id) = self.module()?; + Some(ModuleId { krate: def_map.krate, local_id }) + } + pub fn krate(&self) -> Option { self.module().map(|t| t.0.krate) }