mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 12:29:21 +00:00
Auto merge of #12895 - Veykril:compl-anchor, r=Veykril
fix: Calculate completions after type anchors Fixes https://github.com/rust-lang/rust-analyzer/issues/12892
This commit is contained in:
commit
02f9ec4be5
12 changed files with 151 additions and 36 deletions
|
@ -324,6 +324,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
||||||
self.imp.resolve_type(ty)
|
self.imp.resolve_type(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_trait(&self, trait_: &ast::Path) -> Option<Trait> {
|
||||||
|
self.imp.resolve_trait(trait_)
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Figure out a nice interface to inspect adjustments
|
// FIXME: Figure out a nice interface to inspect adjustments
|
||||||
pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
|
pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
|
||||||
self.imp.is_implicit_reborrow(expr)
|
self.imp.is_implicit_reborrow(expr)
|
||||||
|
@ -1014,6 +1018,20 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
|
Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> {
|
||||||
|
let analyze = self.analyze(path.syntax())?;
|
||||||
|
let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
|
||||||
|
let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
|
||||||
|
let hir_path = Path::from_src(path.clone(), &ctx)?;
|
||||||
|
match analyze
|
||||||
|
.resolver
|
||||||
|
.resolve_path_in_type_ns_fully(self.db.upcast(), hir_path.mod_path())?
|
||||||
|
{
|
||||||
|
TypeNs::TraitId(id) => Some(Trait { id }),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
|
fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
|
||||||
self.analyze(expr.syntax())?.is_implicit_reborrow(self.db, expr)
|
self.analyze(expr.syntax())?.is_implicit_reborrow(self.db, expr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,7 +115,7 @@ pub(crate) fn complete_attribute_path(
|
||||||
});
|
});
|
||||||
acc.add_nameref_keywords_with_colon(ctx);
|
acc.add_nameref_keywords_with_colon(ctx);
|
||||||
}
|
}
|
||||||
Qualified::Infer | Qualified::With { .. } => {}
|
Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let attributes = annotated_item_kind.and_then(|kind| {
|
let attributes = annotated_item_kind.and_then(|kind| {
|
||||||
|
|
|
@ -97,7 +97,7 @@ pub(crate) fn complete_derive_path(
|
||||||
});
|
});
|
||||||
acc.add_nameref_keywords_with_colon(ctx);
|
acc.add_nameref_keywords_with_colon(ctx);
|
||||||
}
|
}
|
||||||
Qualified::Infer | Qualified::With { .. } => {}
|
Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,11 +46,32 @@ pub(crate) fn complete_expr_path(
|
||||||
};
|
};
|
||||||
|
|
||||||
match qualified {
|
match qualified {
|
||||||
Qualified::Infer => ctx
|
Qualified::TypeAnchor { ty: None, trait_: None } => ctx
|
||||||
.traits_in_scope()
|
.traits_in_scope()
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
|
.flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
|
||||||
.for_each(|item| add_assoc_item(acc, item)),
|
.for_each(|item| add_assoc_item(acc, item)),
|
||||||
|
Qualified::TypeAnchor { trait_: Some(trait_), .. } => {
|
||||||
|
trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item))
|
||||||
|
}
|
||||||
|
Qualified::TypeAnchor { ty: Some(ty), trait_: None } => {
|
||||||
|
if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
|
||||||
|
cov_mark::hit!(completes_variant_through_alias);
|
||||||
|
acc.add_enum_variants(ctx, path_ctx, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.iterate_path_candidates(&ty, |item| {
|
||||||
|
add_assoc_item(acc, item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Iterate assoc types separately
|
||||||
|
ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
|
||||||
|
if let hir::AssocItem::TypeAlias(ty) = item {
|
||||||
|
acc.add_type_alias(ctx, ty)
|
||||||
|
}
|
||||||
|
None::<()>
|
||||||
|
});
|
||||||
|
}
|
||||||
Qualified::With { resolution: None, .. } => {}
|
Qualified::With { resolution: None, .. } => {}
|
||||||
Qualified::With { resolution: Some(resolution), .. } => {
|
Qualified::With { resolution: Some(resolution), .. } => {
|
||||||
// Add associated types on type parameters and `Self`.
|
// Add associated types on type parameters and `Self`.
|
||||||
|
|
|
@ -66,7 +66,7 @@ pub(crate) fn complete_item_list(
|
||||||
});
|
});
|
||||||
acc.add_nameref_keywords_with_colon(ctx);
|
acc.add_nameref_keywords_with_colon(ctx);
|
||||||
}
|
}
|
||||||
Qualified::Infer | Qualified::No | Qualified::With { .. } => {}
|
Qualified::TypeAnchor { .. } | Qualified::No | Qualified::With { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,6 +180,6 @@ pub(crate) fn complete_pattern_path(
|
||||||
|
|
||||||
acc.add_nameref_keywords_with_colon(ctx);
|
acc.add_nameref_keywords_with_colon(ctx);
|
||||||
}
|
}
|
||||||
Qualified::Infer | Qualified::With { .. } => {}
|
Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,11 +49,27 @@ pub(crate) fn complete_type_path(
|
||||||
};
|
};
|
||||||
|
|
||||||
match qualified {
|
match qualified {
|
||||||
Qualified::Infer => ctx
|
Qualified::TypeAnchor { ty: None, trait_: None } => ctx
|
||||||
.traits_in_scope()
|
.traits_in_scope()
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
|
.flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
|
||||||
.for_each(|item| add_assoc_item(acc, item)),
|
.for_each(|item| add_assoc_item(acc, item)),
|
||||||
|
Qualified::TypeAnchor { trait_: Some(trait_), .. } => {
|
||||||
|
trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item))
|
||||||
|
}
|
||||||
|
Qualified::TypeAnchor { ty: Some(ty), trait_: None } => {
|
||||||
|
ctx.iterate_path_candidates(&ty, |item| {
|
||||||
|
add_assoc_item(acc, item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Iterate assoc types separately
|
||||||
|
ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
|
||||||
|
if let hir::AssocItem::TypeAlias(ty) = item {
|
||||||
|
acc.add_type_alias(ctx, ty)
|
||||||
|
}
|
||||||
|
None::<()>
|
||||||
|
});
|
||||||
|
}
|
||||||
Qualified::With { resolution: None, .. } => {}
|
Qualified::With { resolution: None, .. } => {}
|
||||||
Qualified::With { resolution: Some(resolution), .. } => {
|
Qualified::With { resolution: Some(resolution), .. } => {
|
||||||
// Add associated types on type parameters and `Self`.
|
// Add associated types on type parameters and `Self`.
|
||||||
|
|
|
@ -115,6 +115,6 @@ pub(crate) fn complete_use_path(
|
||||||
});
|
});
|
||||||
acc.add_nameref_keywords_with_colon(ctx);
|
acc.add_nameref_keywords_with_colon(ctx);
|
||||||
}
|
}
|
||||||
Qualified::Infer | Qualified::With { resolution: None, .. } => {}
|
Qualified::TypeAnchor { .. } | Qualified::With { resolution: None, .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub(crate) fn complete_vis_path(
|
||||||
|
|
||||||
acc.add_super_keyword(ctx, *super_chain_len);
|
acc.add_super_keyword(ctx, *super_chain_len);
|
||||||
}
|
}
|
||||||
Qualified::Absolute | Qualified::Infer | Qualified::With { .. } => {}
|
Qualified::Absolute | Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
|
||||||
Qualified::No => {
|
Qualified::No => {
|
||||||
if !has_in_token {
|
if !has_in_token {
|
||||||
cov_mark::hit!(kw_completion_in);
|
cov_mark::hit!(kw_completion_in);
|
||||||
|
|
|
@ -193,7 +193,10 @@ pub(super) enum Qualified {
|
||||||
super_chain_len: Option<usize>,
|
super_chain_len: Option<usize>,
|
||||||
},
|
},
|
||||||
/// <_>::
|
/// <_>::
|
||||||
Infer,
|
TypeAnchor {
|
||||||
|
ty: Option<hir::Type>,
|
||||||
|
trait_: Option<hir::Trait>,
|
||||||
|
},
|
||||||
/// Whether the path is an absolute path
|
/// Whether the path is an absolute path
|
||||||
Absolute,
|
Absolute,
|
||||||
}
|
}
|
||||||
|
|
|
@ -920,49 +920,53 @@ impl<'a> CompletionContext<'a> {
|
||||||
path_ctx.has_type_args = segment.generic_arg_list().is_some();
|
path_ctx.has_type_args = segment.generic_arg_list().is_some();
|
||||||
|
|
||||||
// calculate the qualifier context
|
// calculate the qualifier context
|
||||||
if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
|
if let Some((qualifier, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
|
||||||
path_ctx.use_tree_parent = use_tree_parent;
|
path_ctx.use_tree_parent = use_tree_parent;
|
||||||
if !use_tree_parent && segment.coloncolon_token().is_some() {
|
if !use_tree_parent && segment.coloncolon_token().is_some() {
|
||||||
path_ctx.qualified = Qualified::Absolute;
|
path_ctx.qualified = Qualified::Absolute;
|
||||||
} else {
|
} else {
|
||||||
let path = path
|
let qualifier = qualifier
|
||||||
.segment()
|
.segment()
|
||||||
.and_then(|it| find_node_in_file(original_file, &it))
|
.and_then(|it| find_node_in_file(original_file, &it))
|
||||||
.map(|it| it.parent_path());
|
.map(|it| it.parent_path());
|
||||||
if let Some(path) = path {
|
if let Some(qualifier) = qualifier {
|
||||||
// `<_>::$0`
|
let type_anchor = match qualifier.segment().and_then(|it| it.kind()) {
|
||||||
let is_infer_qualifier = path.qualifier().is_none()
|
Some(ast::PathSegmentKind::Type {
|
||||||
&& matches!(
|
type_ref: Some(type_ref),
|
||||||
path.segment().and_then(|it| it.kind()),
|
trait_ref,
|
||||||
Some(ast::PathSegmentKind::Type {
|
}) if qualifier.qualifier().is_none() => Some((type_ref, trait_ref)),
|
||||||
type_ref: Some(ast::Type::InferType(_)),
|
_ => None,
|
||||||
trait_ref: None,
|
};
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
path_ctx.qualified = if is_infer_qualifier {
|
path_ctx.qualified = if let Some((ty, trait_ref)) = type_anchor {
|
||||||
Qualified::Infer
|
let ty = match ty {
|
||||||
|
ast::Type::InferType(_) => None,
|
||||||
|
ty => sema.resolve_type(&ty),
|
||||||
|
};
|
||||||
|
let trait_ = trait_ref.and_then(|it| sema.resolve_trait(&it.path()?));
|
||||||
|
Qualified::TypeAnchor { ty, trait_ }
|
||||||
} else {
|
} else {
|
||||||
let res = sema.resolve_path(&path);
|
let res = sema.resolve_path(&qualifier);
|
||||||
|
|
||||||
// For understanding how and why super_chain_len is calculated the way it
|
// For understanding how and why super_chain_len is calculated the way it
|
||||||
// is check the documentation at it's definition
|
// is check the documentation at it's definition
|
||||||
let mut segment_count = 0;
|
let mut segment_count = 0;
|
||||||
let super_count = iter::successors(Some(path.clone()), |p| p.qualifier())
|
let super_count =
|
||||||
.take_while(|p| {
|
iter::successors(Some(qualifier.clone()), |p| p.qualifier())
|
||||||
p.segment()
|
.take_while(|p| {
|
||||||
.and_then(|s| {
|
p.segment()
|
||||||
segment_count += 1;
|
.and_then(|s| {
|
||||||
s.super_token()
|
segment_count += 1;
|
||||||
})
|
s.super_token()
|
||||||
.is_some()
|
})
|
||||||
})
|
.is_some()
|
||||||
.count();
|
})
|
||||||
|
.count();
|
||||||
|
|
||||||
let super_chain_len =
|
let super_chain_len =
|
||||||
if segment_count > super_count { None } else { Some(super_count) };
|
if segment_count > super_count { None } else { Some(super_count) };
|
||||||
|
|
||||||
Qualified::With { path, resolution: res, super_chain_len }
|
Qualified::With { path: qualifier, resolution: res, super_chain_len }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -674,7 +674,60 @@ fn bar() -> Bar {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
fn foo() (as Foo) fn() -> Self
|
fn foo() (as Foo) fn() -> Self
|
||||||
"#]],
|
"#]],
|
||||||
)
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn type_anchor_type() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
trait Foo {
|
||||||
|
fn foo() -> Self;
|
||||||
|
}
|
||||||
|
struct Bar;
|
||||||
|
impl Bar {
|
||||||
|
fn bar() {}
|
||||||
|
}
|
||||||
|
impl Foo for Bar {
|
||||||
|
fn foo() -> {
|
||||||
|
Bar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn bar() -> Bar {
|
||||||
|
<Bar>::$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
fn bar() fn()
|
||||||
|
fn foo() (as Foo) fn() -> Self
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn type_anchor_type_trait() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
trait Foo {
|
||||||
|
fn foo() -> Self;
|
||||||
|
}
|
||||||
|
struct Bar;
|
||||||
|
impl Bar {
|
||||||
|
fn bar() {}
|
||||||
|
}
|
||||||
|
impl Foo for Bar {
|
||||||
|
fn foo() -> {
|
||||||
|
Bar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn bar() -> Bar {
|
||||||
|
<Bar as Foo>::$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
fn foo() (as Foo) fn() -> Self
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue