mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-29 06:23:25 +00:00
made the add_missing_impl_members
and add_missing_default_members
assists transform default generic types
This commit is contained in:
parent
5ce65a1d92
commit
b07490ffe9
2 changed files with 157 additions and 32 deletions
|
@ -830,6 +830,115 @@ impl Foo<T> for S<T> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_qualify_generic_default_parameter() {
|
||||||
|
check_assist(
|
||||||
|
add_missing_impl_members,
|
||||||
|
r#"
|
||||||
|
mod m {
|
||||||
|
pub struct S;
|
||||||
|
pub trait Foo<T = S> {
|
||||||
|
fn bar(&self, other: &T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
impl m::Foo for S { $0 }"#,
|
||||||
|
r#"
|
||||||
|
mod m {
|
||||||
|
pub struct S;
|
||||||
|
pub trait Foo<T = S> {
|
||||||
|
fn bar(&self, other: &T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
impl m::Foo for S {
|
||||||
|
fn bar(&self, other: &m::S) {
|
||||||
|
${0:todo!()}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_qualify_generic_default_parameter_2() {
|
||||||
|
check_assist(
|
||||||
|
add_missing_impl_members,
|
||||||
|
r#"
|
||||||
|
mod m {
|
||||||
|
pub struct Wrapper<T, V> {
|
||||||
|
one: T,
|
||||||
|
another: V
|
||||||
|
};
|
||||||
|
pub struct S;
|
||||||
|
pub trait Foo<T = Wrapper<S, bool>> {
|
||||||
|
fn bar(&self, other: &T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
impl m::Foo for S { $0 }"#,
|
||||||
|
r#"
|
||||||
|
mod m {
|
||||||
|
pub struct Wrapper<T, V> {
|
||||||
|
one: T,
|
||||||
|
another: V
|
||||||
|
};
|
||||||
|
pub struct S;
|
||||||
|
pub trait Foo<T = Wrapper<S, bool>> {
|
||||||
|
fn bar(&self, other: &T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
impl m::Foo for S {
|
||||||
|
fn bar(&self, other: &m::Wrapper<m::S, bool>) {
|
||||||
|
${0:todo!()}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_qualify_generic_default_parameter_3() {
|
||||||
|
check_assist(
|
||||||
|
add_missing_impl_members,
|
||||||
|
r#"
|
||||||
|
mod m {
|
||||||
|
pub struct Wrapper<T, V> {
|
||||||
|
one: T,
|
||||||
|
another: V
|
||||||
|
};
|
||||||
|
pub struct S;
|
||||||
|
pub trait Foo<T = S, V = Wrapper<T, S>> {
|
||||||
|
fn bar(&self, other: &V);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
impl m::Foo for S { $0 }"#,
|
||||||
|
r#"
|
||||||
|
mod m {
|
||||||
|
pub struct Wrapper<T, V> {
|
||||||
|
one: T,
|
||||||
|
another: V
|
||||||
|
};
|
||||||
|
pub struct S;
|
||||||
|
pub trait Foo<T = S, V = Wrapper<T, S>> {
|
||||||
|
fn bar(&self, other: &V);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
impl m::Foo for S {
|
||||||
|
fn bar(&self, other: &m::Wrapper<m::S, m::S>) {
|
||||||
|
${0:todo!()}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_assoc_type_bounds_are_removed() {
|
fn test_assoc_type_bounds_are_removed() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
|
|
@ -17,6 +17,11 @@ struct AstSubsts {
|
||||||
lifetimes: Vec<ast::LifetimeArg>,
|
lifetimes: Vec<ast::LifetimeArg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TypeOrConstSubst {
|
||||||
|
subst: ast::Type,
|
||||||
|
to_be_transformed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
type LifetimeName = String;
|
type LifetimeName = String;
|
||||||
|
|
||||||
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
|
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
|
||||||
|
@ -123,13 +128,21 @@ impl<'a> PathTransform<'a> {
|
||||||
// the resulting change can be applied correctly.
|
// the resulting change can be applied correctly.
|
||||||
.zip(self.substs.types_and_consts.iter().map(Some).chain(std::iter::repeat(None)))
|
.zip(self.substs.types_and_consts.iter().map(Some).chain(std::iter::repeat(None)))
|
||||||
.filter_map(|(k, v)| match (k.split(db), v) {
|
.filter_map(|(k, v)| match (k.split(db), v) {
|
||||||
(_, Some(v)) => Some((k, v.ty()?.clone())),
|
(_, Some(v)) => {
|
||||||
|
let subst =
|
||||||
|
TypeOrConstSubst { subst: v.ty()?.clone(), to_be_transformed: false };
|
||||||
|
Some((k, subst))
|
||||||
|
}
|
||||||
(Either::Right(t), None) => {
|
(Either::Right(t), None) => {
|
||||||
let default = t.default(db)?;
|
let default = t.default(db)?;
|
||||||
let v = ast::make::ty(
|
let subst = TypeOrConstSubst {
|
||||||
|
subst: ast::make::ty(
|
||||||
&default.display_source_code(db, source_module.into(), false).ok()?,
|
&default.display_source_code(db, source_module.into(), false).ok()?,
|
||||||
);
|
)
|
||||||
Some((k, v))
|
.clone_for_update(),
|
||||||
|
to_be_transformed: true,
|
||||||
|
};
|
||||||
|
Some((k, subst))
|
||||||
}
|
}
|
||||||
(Either::Left(_), None) => None, // FIXME: get default const value
|
(Either::Left(_), None) => None, // FIXME: get default const value
|
||||||
})
|
})
|
||||||
|
@ -151,43 +164,42 @@ impl<'a> PathTransform<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Ctx<'a> {
|
struct Ctx<'a> {
|
||||||
type_and_const_substs: FxHashMap<hir::TypeOrConstParam, ast::Type>,
|
type_and_const_substs: FxHashMap<hir::TypeOrConstParam, TypeOrConstSubst>,
|
||||||
lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>,
|
lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>,
|
||||||
target_module: hir::Module,
|
target_module: hir::Module,
|
||||||
source_scope: &'a SemanticsScope<'a>,
|
source_scope: &'a SemanticsScope<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Ctx<'a> {
|
fn preorder(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
|
||||||
fn apply(&self, item: &SyntaxNode) {
|
item.preorder().filter_map(|event| match event {
|
||||||
// `transform_path` may update a node's parent and that would break the
|
|
||||||
// tree traversal. Thus all paths in the tree are collected into a vec
|
|
||||||
// so that such operation is safe.
|
|
||||||
let paths = item
|
|
||||||
.preorder()
|
|
||||||
.filter_map(|event| match event {
|
|
||||||
syntax::WalkEvent::Enter(_) => None,
|
syntax::WalkEvent::Enter(_) => None,
|
||||||
syntax::WalkEvent::Leave(node) => Some(node),
|
syntax::WalkEvent::Leave(node) => Some(node),
|
||||||
})
|
})
|
||||||
.filter_map(ast::Path::cast)
|
}
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
|
impl<'a> Ctx<'a> {
|
||||||
|
fn apply(&self, item: &SyntaxNode) {
|
||||||
|
for (_, subst) in &self.type_and_const_substs {
|
||||||
|
if subst.to_be_transformed {
|
||||||
|
let paths =
|
||||||
|
preorder(&subst.subst.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>();
|
||||||
|
for path in paths {
|
||||||
|
self.transform_path(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `transform_path` may update a node's parent and that would break the
|
||||||
|
// tree traversal. Thus all paths in the tree are collected into a vec
|
||||||
|
// so that such operation is safe.
|
||||||
|
let paths = preorder(item).filter_map(ast::Path::cast).collect::<Vec<_>>();
|
||||||
for path in paths {
|
for path in paths {
|
||||||
self.transform_path(path);
|
self.transform_path(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
item.preorder()
|
preorder(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| {
|
||||||
.filter_map(|event| match event {
|
if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) {
|
||||||
syntax::WalkEvent::Enter(_) => None,
|
ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax());
|
||||||
syntax::WalkEvent::Leave(node) => Some(node),
|
|
||||||
})
|
|
||||||
.filter_map(ast::Lifetime::cast)
|
|
||||||
.for_each(|lifetime| {
|
|
||||||
if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string())
|
|
||||||
{
|
|
||||||
ted::replace(
|
|
||||||
lifetime.syntax(),
|
|
||||||
subst.clone_subtree().clone_for_update().syntax(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -208,7 +220,9 @@ impl<'a> Ctx<'a> {
|
||||||
|
|
||||||
match resolution {
|
match resolution {
|
||||||
hir::PathResolution::TypeParam(tp) => {
|
hir::PathResolution::TypeParam(tp) => {
|
||||||
if let Some(subst) = self.type_and_const_substs.get(&tp.merge()) {
|
if let Some(TypeOrConstSubst { subst, .. }) =
|
||||||
|
self.type_and_const_substs.get(&tp.merge())
|
||||||
|
{
|
||||||
let parent = path.syntax().parent()?;
|
let parent = path.syntax().parent()?;
|
||||||
if let Some(parent) = ast::Path::cast(parent.clone()) {
|
if let Some(parent) = ast::Path::cast(parent.clone()) {
|
||||||
// Path inside path means that there is an associated
|
// Path inside path means that there is an associated
|
||||||
|
@ -276,7 +290,9 @@ impl<'a> Ctx<'a> {
|
||||||
ted::replace(path.syntax(), res.syntax())
|
ted::replace(path.syntax(), res.syntax())
|
||||||
}
|
}
|
||||||
hir::PathResolution::ConstParam(cp) => {
|
hir::PathResolution::ConstParam(cp) => {
|
||||||
if let Some(subst) = self.type_and_const_substs.get(&cp.merge()) {
|
if let Some(TypeOrConstSubst { subst, .. }) =
|
||||||
|
self.type_and_const_substs.get(&cp.merge())
|
||||||
|
{
|
||||||
ted::replace(path.syntax(), subst.clone_subtree().clone_for_update().syntax());
|
ted::replace(path.syntax(), subst.clone_subtree().clone_for_update().syntax());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue