Skip to content

Commit 02f9ec4

Browse files
committed
Auto merge of rust-lang#12895 - Veykril:compl-anchor, r=Veykril
fix: Calculate completions after type anchors Fixes rust-lang/rust-analyzer#12892
2 parents 4087535 + e782e59 commit 02f9ec4

File tree

12 files changed

+151
-36
lines changed

12 files changed

+151
-36
lines changed

crates/hir/src/semantics.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
324324
self.imp.resolve_type(ty)
325325
}
326326

327+
pub fn resolve_trait(&self, trait_: &ast::Path) -> Option<Trait> {
328+
self.imp.resolve_trait(trait_)
329+
}
330+
327331
// FIXME: Figure out a nice interface to inspect adjustments
328332
pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
329333
self.imp.is_implicit_reborrow(expr)
@@ -1014,6 +1018,20 @@ impl<'db> SemanticsImpl<'db> {
10141018
Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
10151019
}
10161020

1021+
fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> {
1022+
let analyze = self.analyze(path.syntax())?;
1023+
let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
1024+
let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
1025+
let hir_path = Path::from_src(path.clone(), &ctx)?;
1026+
match analyze
1027+
.resolver
1028+
.resolve_path_in_type_ns_fully(self.db.upcast(), hir_path.mod_path())?
1029+
{
1030+
TypeNs::TraitId(id) => Some(Trait { id }),
1031+
_ => None,
1032+
}
1033+
}
1034+
10171035
fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
10181036
self.analyze(expr.syntax())?.is_implicit_reborrow(self.db, expr)
10191037
}

crates/ide-completion/src/completions/attribute.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ pub(crate) fn complete_attribute_path(
115115
});
116116
acc.add_nameref_keywords_with_colon(ctx);
117117
}
118-
Qualified::Infer | Qualified::With { .. } => {}
118+
Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
119119
}
120120

121121
let attributes = annotated_item_kind.and_then(|kind| {

crates/ide-completion/src/completions/attribute/derive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub(crate) fn complete_derive_path(
9797
});
9898
acc.add_nameref_keywords_with_colon(ctx);
9999
}
100-
Qualified::Infer | Qualified::With { .. } => {}
100+
Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
101101
}
102102
}
103103

crates/ide-completion/src/completions/expr.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,32 @@ pub(crate) fn complete_expr_path(
4646
};
4747

4848
match qualified {
49-
Qualified::Infer => ctx
49+
Qualified::TypeAnchor { ty: None, trait_: None } => ctx
5050
.traits_in_scope()
5151
.iter()
5252
.flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
5353
.for_each(|item| add_assoc_item(acc, item)),
54+
Qualified::TypeAnchor { trait_: Some(trait_), .. } => {
55+
trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item))
56+
}
57+
Qualified::TypeAnchor { ty: Some(ty), trait_: None } => {
58+
if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
59+
cov_mark::hit!(completes_variant_through_alias);
60+
acc.add_enum_variants(ctx, path_ctx, e);
61+
}
62+
63+
ctx.iterate_path_candidates(&ty, |item| {
64+
add_assoc_item(acc, item);
65+
});
66+
67+
// Iterate assoc types separately
68+
ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
69+
if let hir::AssocItem::TypeAlias(ty) = item {
70+
acc.add_type_alias(ctx, ty)
71+
}
72+
None::<()>
73+
});
74+
}
5475
Qualified::With { resolution: None, .. } => {}
5576
Qualified::With { resolution: Some(resolution), .. } => {
5677
// Add associated types on type parameters and `Self`.

crates/ide-completion/src/completions/item_list.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub(crate) fn complete_item_list(
6666
});
6767
acc.add_nameref_keywords_with_colon(ctx);
6868
}
69-
Qualified::Infer | Qualified::No | Qualified::With { .. } => {}
69+
Qualified::TypeAnchor { .. } | Qualified::No | Qualified::With { .. } => {}
7070
}
7171
}
7272

crates/ide-completion/src/completions/pattern.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,6 @@ pub(crate) fn complete_pattern_path(
180180

181181
acc.add_nameref_keywords_with_colon(ctx);
182182
}
183-
Qualified::Infer | Qualified::With { .. } => {}
183+
Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
184184
}
185185
}

crates/ide-completion/src/completions/type.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,27 @@ pub(crate) fn complete_type_path(
4949
};
5050

5151
match qualified {
52-
Qualified::Infer => ctx
52+
Qualified::TypeAnchor { ty: None, trait_: None } => ctx
5353
.traits_in_scope()
5454
.iter()
5555
.flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
5656
.for_each(|item| add_assoc_item(acc, item)),
57+
Qualified::TypeAnchor { trait_: Some(trait_), .. } => {
58+
trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item))
59+
}
60+
Qualified::TypeAnchor { ty: Some(ty), trait_: None } => {
61+
ctx.iterate_path_candidates(&ty, |item| {
62+
add_assoc_item(acc, item);
63+
});
64+
65+
// Iterate assoc types separately
66+
ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
67+
if let hir::AssocItem::TypeAlias(ty) = item {
68+
acc.add_type_alias(ctx, ty)
69+
}
70+
None::<()>
71+
});
72+
}
5773
Qualified::With { resolution: None, .. } => {}
5874
Qualified::With { resolution: Some(resolution), .. } => {
5975
// Add associated types on type parameters and `Self`.

crates/ide-completion/src/completions/use_.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,6 @@ pub(crate) fn complete_use_path(
115115
});
116116
acc.add_nameref_keywords_with_colon(ctx);
117117
}
118-
Qualified::Infer | Qualified::With { resolution: None, .. } => {}
118+
Qualified::TypeAnchor { .. } | Qualified::With { resolution: None, .. } => {}
119119
}
120120
}

crates/ide-completion/src/completions/vis.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ pub(crate) fn complete_vis_path(
2929

3030
acc.add_super_keyword(ctx, *super_chain_len);
3131
}
32-
Qualified::Absolute | Qualified::Infer | Qualified::With { .. } => {}
32+
Qualified::Absolute | Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
3333
Qualified::No => {
3434
if !has_in_token {
3535
cov_mark::hit!(kw_completion_in);

crates/ide-completion/src/context.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,10 @@ pub(super) enum Qualified {
193193
super_chain_len: Option<usize>,
194194
},
195195
/// <_>::
196-
Infer,
196+
TypeAnchor {
197+
ty: Option<hir::Type>,
198+
trait_: Option<hir::Trait>,
199+
},
197200
/// Whether the path is an absolute path
198201
Absolute,
199202
}

crates/ide-completion/src/context/analysis.rs

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -920,49 +920,53 @@ impl<'a> CompletionContext<'a> {
920920
path_ctx.has_type_args = segment.generic_arg_list().is_some();
921921

922922
// calculate the qualifier context
923-
if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
923+
if let Some((qualifier, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
924924
path_ctx.use_tree_parent = use_tree_parent;
925925
if !use_tree_parent && segment.coloncolon_token().is_some() {
926926
path_ctx.qualified = Qualified::Absolute;
927927
} else {
928-
let path = path
928+
let qualifier = qualifier
929929
.segment()
930930
.and_then(|it| find_node_in_file(original_file, &it))
931931
.map(|it| it.parent_path());
932-
if let Some(path) = path {
933-
// `<_>::$0`
934-
let is_infer_qualifier = path.qualifier().is_none()
935-
&& matches!(
936-
path.segment().and_then(|it| it.kind()),
937-
Some(ast::PathSegmentKind::Type {
938-
type_ref: Some(ast::Type::InferType(_)),
939-
trait_ref: None,
940-
})
941-
);
932+
if let Some(qualifier) = qualifier {
933+
let type_anchor = match qualifier.segment().and_then(|it| it.kind()) {
934+
Some(ast::PathSegmentKind::Type {
935+
type_ref: Some(type_ref),
936+
trait_ref,
937+
}) if qualifier.qualifier().is_none() => Some((type_ref, trait_ref)),
938+
_ => None,
939+
};
942940

943-
path_ctx.qualified = if is_infer_qualifier {
944-
Qualified::Infer
941+
path_ctx.qualified = if let Some((ty, trait_ref)) = type_anchor {
942+
let ty = match ty {
943+
ast::Type::InferType(_) => None,
944+
ty => sema.resolve_type(&ty),
945+
};
946+
let trait_ = trait_ref.and_then(|it| sema.resolve_trait(&it.path()?));
947+
Qualified::TypeAnchor { ty, trait_ }
945948
} else {
946-
let res = sema.resolve_path(&path);
949+
let res = sema.resolve_path(&qualifier);
947950

948951
// For understanding how and why super_chain_len is calculated the way it
949952
// is check the documentation at it's definition
950953
let mut segment_count = 0;
951-
let super_count = iter::successors(Some(path.clone()), |p| p.qualifier())
952-
.take_while(|p| {
953-
p.segment()
954-
.and_then(|s| {
955-
segment_count += 1;
956-
s.super_token()
957-
})
958-
.is_some()
959-
})
960-
.count();
954+
let super_count =
955+
iter::successors(Some(qualifier.clone()), |p| p.qualifier())
956+
.take_while(|p| {
957+
p.segment()
958+
.and_then(|s| {
959+
segment_count += 1;
960+
s.super_token()
961+
})
962+
.is_some()
963+
})
964+
.count();
961965

962966
let super_chain_len =
963967
if segment_count > super_count { None } else { Some(super_count) };
964968

965-
Qualified::With { path, resolution: res, super_chain_len }
969+
Qualified::With { path: qualifier, resolution: res, super_chain_len }
966970
}
967971
};
968972
}

crates/ide-completion/src/tests/special.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,60 @@ fn bar() -> Bar {
674674
expect![[r#"
675675
fn foo() (as Foo) fn() -> Self
676676
"#]],
677-
)
677+
);
678+
}
679+
680+
#[test]
681+
fn type_anchor_type() {
682+
check(
683+
r#"
684+
trait Foo {
685+
fn foo() -> Self;
686+
}
687+
struct Bar;
688+
impl Bar {
689+
fn bar() {}
690+
}
691+
impl Foo for Bar {
692+
fn foo() -> {
693+
Bar
694+
}
695+
}
696+
fn bar() -> Bar {
697+
<Bar>::$0
698+
}
699+
"#,
700+
expect![[r#"
701+
fn bar() fn()
702+
fn foo() (as Foo) fn() -> Self
703+
"#]],
704+
);
705+
}
706+
707+
#[test]
708+
fn type_anchor_type_trait() {
709+
check(
710+
r#"
711+
trait Foo {
712+
fn foo() -> Self;
713+
}
714+
struct Bar;
715+
impl Bar {
716+
fn bar() {}
717+
}
718+
impl Foo for Bar {
719+
fn foo() -> {
720+
Bar
721+
}
722+
}
723+
fn bar() -> Bar {
724+
<Bar as Foo>::$0
725+
}
726+
"#,
727+
expect![[r#"
728+
fn foo() (as Foo) fn() -> Self
729+
"#]],
730+
);
678731
}
679732

680733
#[test]

0 commit comments

Comments
 (0)