Qualify some paths in 'add missing impl members'

This commit is contained in:
Florian Diebold 2019-12-31 16:17:08 +01:00
parent 460fa71c55
commit 4d75430e91
6 changed files with 128 additions and 3 deletions

View file

@ -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<N: AstNode>(
}
}
use hir::PathResolution;
// TODO handle partial paths, with generic args
// TODO handle value ns?
fn qualify_paths<N: AstNode>(db: &impl HirDatabase, node: hir::InFile<N>, 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::<Vec<_>>();
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;

View file

@ -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<hir_def::path::ModPath> {
// 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)]

View file

@ -91,6 +91,22 @@ impl From<ModuleDefId> for ModuleDef {
}
}
impl From<ModuleDef> 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<DefWithBody> for DefWithBodyId {
fn from(def: DefWithBody) -> Self {
match def {

View file

@ -205,6 +205,10 @@ impl SourceAnalyzer {
}
}
pub fn module(&self) -> Option<crate::code_model::Module> {
Some(crate::code_model::Module { id: self.resolver.module_id()? })
}
fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> {
let src = InFile { file_id: self.file_id, value: expr };
self.body_source_map.as_ref()?.node_expr(src)

View file

@ -1,7 +1,7 @@
//! A desugared representation of paths like `crate::foo` or `<Type as Trait>::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<Name> 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]

View file

@ -411,6 +411,11 @@ impl Resolver {
})
}
pub fn module_id(&self) -> Option<ModuleId> {
let (def_map, local_id) = self.module()?;
Some(ModuleId { krate: def_map.krate, local_id })
}
pub fn krate(&self) -> Option<CrateId> {
self.module().map(|t| t.0.krate)
}