Qualify some paths in 'add missing impl members'
This commit is contained in:
parent
460fa71c55
commit
4d75430e91
6 changed files with 128 additions and 3 deletions
|
@ -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;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue