mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 22:54:58 +00:00
⬆️ rust-analyzer
This commit is contained in:
parent
a1f1b95d00
commit
9d2cb42a41
45 changed files with 766 additions and 242 deletions
|
@ -400,7 +400,7 @@ impl Completions {
|
|||
) {
|
||||
if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx {
|
||||
cov_mark::hit!(enum_variant_pattern_path);
|
||||
self.add_variant_pat(ctx, pat_ctx, variant, local_name);
|
||||
self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -484,12 +484,14 @@ impl Completions {
|
|||
&mut self,
|
||||
ctx: &CompletionContext<'_>,
|
||||
pattern_ctx: &PatternContext,
|
||||
path_ctx: Option<&PathCompletionCtx>,
|
||||
variant: hir::Variant,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
self.add_opt(render_variant_pat(
|
||||
RenderContext::new(ctx),
|
||||
pattern_ctx,
|
||||
path_ctx,
|
||||
variant,
|
||||
local_name.clone(),
|
||||
None,
|
||||
|
@ -504,7 +506,14 @@ impl Completions {
|
|||
path: hir::ModPath,
|
||||
) {
|
||||
let path = Some(&path);
|
||||
self.add_opt(render_variant_pat(RenderContext::new(ctx), pattern_ctx, variant, None, path));
|
||||
self.add_opt(render_variant_pat(
|
||||
RenderContext::new(ctx),
|
||||
pattern_ctx,
|
||||
None,
|
||||
variant,
|
||||
None,
|
||||
path,
|
||||
));
|
||||
}
|
||||
|
||||
pub(crate) fn add_struct_pat(
|
||||
|
|
|
@ -115,7 +115,7 @@ pub(crate) fn complete_attribute_path(
|
|||
});
|
||||
acc.add_nameref_keywords_with_colon(ctx);
|
||||
}
|
||||
Qualified::Infer | Qualified::With { .. } => {}
|
||||
Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
Qualified::Infer | Qualified::With { .. } => {}
|
||||
Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,14 @@ pub(crate) fn complete_expr_path(
|
|||
acc: &mut Completions,
|
||||
ctx: &CompletionContext<'_>,
|
||||
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
|
||||
&ExprCtx {
|
||||
expr_ctx: &ExprCtx,
|
||||
) {
|
||||
let _p = profile::span("complete_expr_path");
|
||||
if !ctx.qualifier_ctx.none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let &ExprCtx {
|
||||
in_block_expr,
|
||||
in_loop_body,
|
||||
after_if_expr,
|
||||
|
@ -23,12 +30,7 @@ pub(crate) fn complete_expr_path(
|
|||
ref impl_,
|
||||
in_match_guard,
|
||||
..
|
||||
}: &ExprCtx,
|
||||
) {
|
||||
let _p = profile::span("complete_expr_path");
|
||||
if !ctx.qualifier_ctx.none() {
|
||||
return;
|
||||
}
|
||||
} = expr_ctx;
|
||||
|
||||
let wants_mut_token =
|
||||
ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
|
||||
|
@ -46,11 +48,32 @@ pub(crate) fn complete_expr_path(
|
|||
};
|
||||
|
||||
match qualified {
|
||||
Qualified::Infer => ctx
|
||||
Qualified::TypeAnchor { ty: None, trait_: None } => ctx
|
||||
.traits_in_scope()
|
||||
.iter()
|
||||
.flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
|
||||
.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: Some(resolution), .. } => {
|
||||
// Add associated types on type parameters and `Self`.
|
||||
|
@ -179,10 +202,21 @@ pub(crate) fn complete_expr_path(
|
|||
}
|
||||
}
|
||||
}
|
||||
ctx.process_all_names(&mut |name, def| {
|
||||
if scope_def_applicable(def) {
|
||||
acc.add_path_resolution(ctx, path_ctx, name, def);
|
||||
ctx.process_all_names(&mut |name, def| match def {
|
||||
ScopeDef::ModuleDef(hir::ModuleDef::Trait(t)) => {
|
||||
let assocs = t.items_with_supertraits(ctx.db);
|
||||
match &*assocs {
|
||||
// traits with no assoc items are unusable as expressions since
|
||||
// there is no associated item path that can be constructed with them
|
||||
[] => (),
|
||||
// FIXME: Render the assoc item with the trait qualified
|
||||
&[_item] => acc.add_path_resolution(ctx, path_ctx, name, def),
|
||||
// FIXME: Append `::` to the thing here, since a trait on its own won't work
|
||||
[..] => acc.add_path_resolution(ctx, path_ctx, name, def),
|
||||
}
|
||||
}
|
||||
_ if scope_def_applicable(def) => acc.add_path_resolution(ctx, path_ctx, name, def),
|
||||
_ => (),
|
||||
});
|
||||
|
||||
if is_func_update.is_none() {
|
||||
|
|
|
@ -66,7 +66,7 @@ pub(crate) fn complete_item_list(
|
|||
});
|
||||
acc.add_nameref_keywords_with_colon(ctx);
|
||||
}
|
||||
Qualified::Infer | Qualified::No | Qualified::With { .. } => {}
|
||||
Qualified::TypeAnchor { .. } | Qualified::No | Qualified::With { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ pub(crate) fn complete_pattern(
|
|||
hir::ModuleDef::Variant(variant)
|
||||
if refutable || single_variant_enum(variant.parent_enum(ctx.db)) =>
|
||||
{
|
||||
acc.add_variant_pat(ctx, pattern_ctx, variant, Some(name.clone()));
|
||||
acc.add_variant_pat(ctx, pattern_ctx, None, variant, Some(name.clone()));
|
||||
true
|
||||
}
|
||||
hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
|
||||
|
@ -180,6 +180,6 @@ pub(crate) fn complete_pattern_path(
|
|||
|
||||
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 {
|
||||
Qualified::Infer => ctx
|
||||
Qualified::TypeAnchor { ty: None, trait_: None } => ctx
|
||||
.traits_in_scope()
|
||||
.iter()
|
||||
.flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db))
|
||||
.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: Some(resolution), .. } => {
|
||||
// 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
Qualified::Absolute | Qualified::Infer | Qualified::With { .. } => {}
|
||||
Qualified::Absolute | Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
|
||||
Qualified::No => {
|
||||
if !has_in_token {
|
||||
cov_mark::hit!(kw_completion_in);
|
||||
|
|
|
@ -193,7 +193,10 @@ pub(super) enum Qualified {
|
|||
super_chain_len: Option<usize>,
|
||||
},
|
||||
/// <_>::
|
||||
Infer,
|
||||
TypeAnchor {
|
||||
ty: Option<hir::Type>,
|
||||
trait_: Option<hir::Trait>,
|
||||
},
|
||||
/// Whether the path is an absolute path
|
||||
Absolute,
|
||||
}
|
||||
|
|
|
@ -162,11 +162,52 @@ impl<'a> CompletionContext<'a> {
|
|||
}
|
||||
|
||||
/// Calculate the expected type and name of the cursor position.
|
||||
fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
|
||||
fn expected_type_and_name(
|
||||
&self,
|
||||
name_like: &ast::NameLike,
|
||||
) -> (Option<Type>, Option<NameOrNameRef>) {
|
||||
let mut node = match self.token.parent() {
|
||||
Some(it) => it,
|
||||
None => return (None, None),
|
||||
};
|
||||
|
||||
let strip_refs = |mut ty: Type| match name_like {
|
||||
ast::NameLike::NameRef(n) => {
|
||||
let p = match n.syntax().parent() {
|
||||
Some(it) => it,
|
||||
None => return ty,
|
||||
};
|
||||
let top_syn = match_ast! {
|
||||
match p {
|
||||
ast::FieldExpr(e) => e
|
||||
.syntax()
|
||||
.ancestors()
|
||||
.map_while(ast::FieldExpr::cast)
|
||||
.last()
|
||||
.map(|it| it.syntax().clone()),
|
||||
ast::PathSegment(e) => e
|
||||
.syntax()
|
||||
.ancestors()
|
||||
.skip(1)
|
||||
.take_while(|it| ast::Path::can_cast(it.kind()) || ast::PathExpr::can_cast(it.kind()))
|
||||
.find_map(ast::PathExpr::cast)
|
||||
.map(|it| it.syntax().clone()),
|
||||
_ => None
|
||||
}
|
||||
};
|
||||
let top_syn = match top_syn {
|
||||
Some(it) => it,
|
||||
None => return ty,
|
||||
};
|
||||
for _ in top_syn.ancestors().skip(1).map_while(ast::RefExpr::cast) {
|
||||
cov_mark::hit!(expected_type_fn_param_ref);
|
||||
ty = ty.strip_reference();
|
||||
}
|
||||
ty
|
||||
}
|
||||
_ => ty,
|
||||
};
|
||||
|
||||
loop {
|
||||
break match_ast! {
|
||||
match node {
|
||||
|
@ -199,13 +240,9 @@ impl<'a> CompletionContext<'a> {
|
|||
self.token.clone(),
|
||||
).map(|ap| {
|
||||
let name = ap.ident().map(NameOrNameRef::Name);
|
||||
let ty = if has_ref(&self.token) {
|
||||
cov_mark::hit!(expected_type_fn_param_ref);
|
||||
ap.ty.remove_ref()
|
||||
} else {
|
||||
Some(ap.ty)
|
||||
};
|
||||
(ty, name)
|
||||
|
||||
let ty = strip_refs(ap.ty);
|
||||
(Some(ty), name)
|
||||
})
|
||||
.unwrap_or((None, None))
|
||||
},
|
||||
|
@ -330,8 +367,6 @@ impl<'a> CompletionContext<'a> {
|
|||
return None;
|
||||
}
|
||||
|
||||
(self.expected_type, self.expected_name) = self.expected_type_and_name();
|
||||
|
||||
// Overwrite the path kind for derives
|
||||
if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
|
||||
if let Some(ast::NameLike::NameRef(name_ref)) =
|
||||
|
@ -389,6 +424,7 @@ impl<'a> CompletionContext<'a> {
|
|||
return Some(analysis);
|
||||
}
|
||||
};
|
||||
(self.expected_type, self.expected_name) = self.expected_type_and_name(&name_like);
|
||||
let analysis = match name_like {
|
||||
ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime(
|
||||
Self::classify_lifetime(&self.sema, original_file, lifetime)?,
|
||||
|
@ -556,7 +592,7 @@ impl<'a> CompletionContext<'a> {
|
|||
has_call_parens: false,
|
||||
has_macro_bang: false,
|
||||
qualified: Qualified::No,
|
||||
parent: path.parent_path(),
|
||||
parent: None,
|
||||
path: path.clone(),
|
||||
kind: PathKind::Item { kind: ItemListKind::SourceFile },
|
||||
has_type_args: false,
|
||||
|
@ -791,92 +827,125 @@ impl<'a> CompletionContext<'a> {
|
|||
PathKind::Type { location: location.unwrap_or(TypeLocation::Other) }
|
||||
};
|
||||
|
||||
let mut kind_macro_call = |it: ast::MacroCall| {
|
||||
path_ctx.has_macro_bang = it.excl_token().is_some();
|
||||
let parent = it.syntax().parent()?;
|
||||
// Any path in an item list will be treated as a macro call by the parser
|
||||
let kind = match_ast! {
|
||||
match parent {
|
||||
ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
|
||||
ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
|
||||
ast::MacroType(ty) => make_path_kind_type(ty.into()),
|
||||
ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
|
||||
ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
|
||||
Some(it) => match_ast! {
|
||||
match it {
|
||||
ast::Trait(_) => ItemListKind::Trait,
|
||||
ast::Impl(it) => if it.trait_().is_some() {
|
||||
ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it))
|
||||
} else {
|
||||
ItemListKind::Impl
|
||||
},
|
||||
_ => return None
|
||||
}
|
||||
},
|
||||
None => return None,
|
||||
} },
|
||||
ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
|
||||
ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
Some(kind)
|
||||
};
|
||||
let make_path_kind_attr = |meta: ast::Meta| {
|
||||
let attr = meta.parent_attr()?;
|
||||
let kind = attr.kind();
|
||||
let attached = attr.syntax().parent()?;
|
||||
let is_trailing_outer_attr = kind != AttrKind::Inner
|
||||
&& non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next)
|
||||
.is_none();
|
||||
let annotated_item_kind =
|
||||
if is_trailing_outer_attr { None } else { Some(attached.kind()) };
|
||||
Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } })
|
||||
};
|
||||
|
||||
// Infer the path kind
|
||||
let parent = path.syntax().parent()?;
|
||||
let kind = match_ast! {
|
||||
match parent {
|
||||
ast::PathType(it) => make_path_kind_type(it.into()),
|
||||
ast::PathExpr(it) => {
|
||||
if let Some(p) = it.syntax().parent() {
|
||||
if ast::ExprStmt::can_cast(p.kind()) {
|
||||
if let Some(kind) = inbetween_body_and_decl_check(p) {
|
||||
return Some(make_res(NameRefKind::Keyword(kind)));
|
||||
}
|
||||
match parent {
|
||||
ast::PathType(it) => make_path_kind_type(it.into()),
|
||||
ast::PathExpr(it) => {
|
||||
if let Some(p) = it.syntax().parent() {
|
||||
if ast::ExprStmt::can_cast(p.kind()) {
|
||||
if let Some(kind) = inbetween_body_and_decl_check(p) {
|
||||
return Some(make_res(NameRefKind::Keyword(kind)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
|
||||
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
|
||||
|
||||
make_path_kind_expr(it.into())
|
||||
},
|
||||
ast::TupleStructPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
|
||||
},
|
||||
ast::RecordPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
|
||||
},
|
||||
ast::PathPat(it) => {
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
|
||||
},
|
||||
ast::MacroCall(it) => {
|
||||
// A macro call in this position is usually a result of parsing recovery, so check that
|
||||
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
|
||||
return Some(make_res(NameRefKind::Keyword(kind)));
|
||||
make_path_kind_expr(it.into())
|
||||
},
|
||||
ast::TupleStructPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
|
||||
},
|
||||
ast::RecordPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
|
||||
},
|
||||
ast::PathPat(it) => {
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
|
||||
},
|
||||
ast::MacroCall(it) => {
|
||||
// A macro call in this position is usually a result of parsing recovery, so check that
|
||||
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
|
||||
return Some(make_res(NameRefKind::Keyword(kind)));
|
||||
}
|
||||
|
||||
kind_macro_call(it)?
|
||||
},
|
||||
ast::Meta(meta) => make_path_kind_attr(meta)?,
|
||||
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
|
||||
ast::UseTree(_) => PathKind::Use,
|
||||
// completing inside a qualifier
|
||||
ast::Path(parent) => {
|
||||
path_ctx.parent = Some(parent.clone());
|
||||
let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?;
|
||||
match_ast! {
|
||||
match parent {
|
||||
ast::PathType(it) => make_path_kind_type(it.into()),
|
||||
ast::PathExpr(it) => {
|
||||
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
|
||||
|
||||
make_path_kind_expr(it.into())
|
||||
},
|
||||
ast::TupleStructPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
|
||||
},
|
||||
ast::RecordPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
|
||||
},
|
||||
ast::PathPat(it) => {
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
|
||||
},
|
||||
ast::MacroCall(it) => {
|
||||
kind_macro_call(it)?
|
||||
},
|
||||
ast::Meta(meta) => make_path_kind_attr(meta)?,
|
||||
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
|
||||
ast::UseTree(_) => PathKind::Use,
|
||||
ast::RecordExpr(it) => make_path_kind_expr(it.into()),
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
path_ctx.has_macro_bang = it.excl_token().is_some();
|
||||
let parent = it.syntax().parent()?;
|
||||
// Any path in an item list will be treated as a macro call by the parser
|
||||
match_ast! {
|
||||
match parent {
|
||||
ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
|
||||
ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
|
||||
ast::MacroType(ty) => make_path_kind_type(ty.into()),
|
||||
ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
|
||||
ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
|
||||
Some(it) => match_ast! {
|
||||
match it {
|
||||
ast::Trait(_) => ItemListKind::Trait,
|
||||
ast::Impl(it) => if it.trait_().is_some() {
|
||||
ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it))
|
||||
} else {
|
||||
ItemListKind::Impl
|
||||
},
|
||||
_ => return None
|
||||
}
|
||||
},
|
||||
None => return None,
|
||||
} },
|
||||
ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
|
||||
ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
ast::Meta(meta) => {
|
||||
let attr = meta.parent_attr()?;
|
||||
let kind = attr.kind();
|
||||
let attached = attr.syntax().parent()?;
|
||||
let is_trailing_outer_attr = kind != AttrKind::Inner
|
||||
&& non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
|
||||
let annotated_item_kind = if is_trailing_outer_attr {
|
||||
None
|
||||
} else {
|
||||
Some(attached.kind())
|
||||
};
|
||||
PathKind::Attr {
|
||||
attr_ctx: AttrCtx {
|
||||
kind,
|
||||
annotated_item_kind,
|
||||
}
|
||||
}
|
||||
},
|
||||
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
|
||||
ast::UseTree(_) => PathKind::Use,
|
||||
_ => return None,
|
||||
|
||||
}
|
||||
},
|
||||
ast::RecordExpr(it) => make_path_kind_expr(it.into()),
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -884,49 +953,53 @@ impl<'a> CompletionContext<'a> {
|
|||
path_ctx.has_type_args = segment.generic_arg_list().is_some();
|
||||
|
||||
// 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;
|
||||
if !use_tree_parent && segment.coloncolon_token().is_some() {
|
||||
path_ctx.qualified = Qualified::Absolute;
|
||||
} else {
|
||||
let path = path
|
||||
let qualifier = qualifier
|
||||
.segment()
|
||||
.and_then(|it| find_node_in_file(original_file, &it))
|
||||
.map(|it| it.parent_path());
|
||||
if let Some(path) = path {
|
||||
// `<_>::$0`
|
||||
let is_infer_qualifier = path.qualifier().is_none()
|
||||
&& matches!(
|
||||
path.segment().and_then(|it| it.kind()),
|
||||
Some(ast::PathSegmentKind::Type {
|
||||
type_ref: Some(ast::Type::InferType(_)),
|
||||
trait_ref: None,
|
||||
})
|
||||
);
|
||||
if let Some(qualifier) = qualifier {
|
||||
let type_anchor = match qualifier.segment().and_then(|it| it.kind()) {
|
||||
Some(ast::PathSegmentKind::Type {
|
||||
type_ref: Some(type_ref),
|
||||
trait_ref,
|
||||
}) if qualifier.qualifier().is_none() => Some((type_ref, trait_ref)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
path_ctx.qualified = if is_infer_qualifier {
|
||||
Qualified::Infer
|
||||
path_ctx.qualified = if let Some((ty, trait_ref)) = type_anchor {
|
||||
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 {
|
||||
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
|
||||
// is check the documentation at it's definition
|
||||
let mut segment_count = 0;
|
||||
let super_count = iter::successors(Some(path.clone()), |p| p.qualifier())
|
||||
.take_while(|p| {
|
||||
p.segment()
|
||||
.and_then(|s| {
|
||||
segment_count += 1;
|
||||
s.super_token()
|
||||
})
|
||||
.is_some()
|
||||
})
|
||||
.count();
|
||||
let super_count =
|
||||
iter::successors(Some(qualifier.clone()), |p| p.qualifier())
|
||||
.take_while(|p| {
|
||||
p.segment()
|
||||
.and_then(|s| {
|
||||
segment_count += 1;
|
||||
s.super_token()
|
||||
})
|
||||
.is_some()
|
||||
})
|
||||
.count();
|
||||
|
||||
let super_chain_len =
|
||||
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 }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1141,19 +1214,6 @@ fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
|
|||
Some((use_tree.path()?, true))
|
||||
}
|
||||
|
||||
fn has_ref(token: &SyntaxToken) -> bool {
|
||||
let mut token = token.clone();
|
||||
for skip in [SyntaxKind::IDENT, SyntaxKind::WHITESPACE, T![mut]] {
|
||||
if token.kind() == skip {
|
||||
token = match token.prev_token() {
|
||||
Some(it) => it,
|
||||
None => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
token.kind() == T![&]
|
||||
}
|
||||
|
||||
pub(crate) fn is_in_token_of_for_loop(element: SyntaxElement) -> bool {
|
||||
// oh my ...
|
||||
(|| {
|
||||
|
|
|
@ -391,3 +391,23 @@ fn foo($0: Foo) {}
|
|||
expect![[r#"ty: ?, name: ?"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expected_type_ref_prefix_on_field() {
|
||||
check_expected_type_and_name(
|
||||
r#"
|
||||
fn foo(_: &mut i32) {}
|
||||
struct S {
|
||||
field: i32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = S {
|
||||
field: 100,
|
||||
};
|
||||
foo(&mut s.f$0);
|
||||
}
|
||||
"#,
|
||||
expect!["ty: i32, name: ?"],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1271,8 +1271,8 @@ fn main() {
|
|||
st S []
|
||||
st &mut S [type]
|
||||
st S []
|
||||
fn main() []
|
||||
fn foo(…) []
|
||||
fn main() []
|
||||
"#]],
|
||||
);
|
||||
check_relevance(
|
||||
|
@ -1288,8 +1288,8 @@ fn main() {
|
|||
lc s [type+name+local]
|
||||
st S [type]
|
||||
st S []
|
||||
fn main() []
|
||||
fn foo(…) []
|
||||
fn main() []
|
||||
"#]],
|
||||
);
|
||||
check_relevance(
|
||||
|
@ -1305,8 +1305,8 @@ fn main() {
|
|||
lc ssss [type+local]
|
||||
st S [type]
|
||||
st S []
|
||||
fn main() []
|
||||
fn foo(…) []
|
||||
fn main() []
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1342,12 +1342,11 @@ fn main() {
|
|||
lc &t [type+local]
|
||||
st S []
|
||||
st &S [type]
|
||||
st T []
|
||||
st S []
|
||||
fn main() []
|
||||
st T []
|
||||
fn foo(…) []
|
||||
fn main() []
|
||||
md core []
|
||||
tt Sized []
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
@ -1389,12 +1388,11 @@ fn main() {
|
|||
lc &mut t [type+local]
|
||||
st S []
|
||||
st &mut S [type]
|
||||
st T []
|
||||
st S []
|
||||
fn main() []
|
||||
st T []
|
||||
fn foo(…) []
|
||||
fn main() []
|
||||
md core []
|
||||
tt Sized []
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
@ -1485,14 +1483,13 @@ fn main() {
|
|||
expect![[r#"
|
||||
st S []
|
||||
st &S [type]
|
||||
st T []
|
||||
st S []
|
||||
fn main() []
|
||||
st T []
|
||||
fn bar() []
|
||||
fn &bar() [type]
|
||||
fn foo(…) []
|
||||
fn main() []
|
||||
md core []
|
||||
tt Sized []
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
@ -1636,8 +1633,8 @@ fn foo() {
|
|||
ev Foo::B [type_could_unify]
|
||||
fn foo() []
|
||||
en Foo []
|
||||
fn baz() []
|
||||
fn bar() []
|
||||
fn baz() []
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1727,9 +1724,9 @@ fn f() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
md std []
|
||||
st Buffer []
|
||||
fn f() []
|
||||
md std []
|
||||
tt BufRead (use std::io::BufRead) [requires_import]
|
||||
st BufReader (use std::io::BufReader) [requires_import]
|
||||
st BufWriter (use std::io::BufWriter) [requires_import]
|
||||
|
|
|
@ -85,7 +85,9 @@ fn render(
|
|||
item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
|
||||
}
|
||||
FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
|
||||
item.ref_match(ref_match, receiver.syntax().text_range().start());
|
||||
if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) {
|
||||
item.ref_match(ref_match, original_expr.syntax().text_range().start());
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use itertools::Itertools;
|
|||
use syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
context::{ParamContext, ParamKind, PatternContext},
|
||||
context::{ParamContext, ParamKind, PathCompletionCtx, PatternContext},
|
||||
render::{
|
||||
variant::{format_literal_label, visible_fields},
|
||||
RenderContext,
|
||||
|
@ -42,6 +42,7 @@ pub(crate) fn render_struct_pat(
|
|||
pub(crate) fn render_variant_pat(
|
||||
ctx: RenderContext<'_>,
|
||||
pattern_ctx: &PatternContext,
|
||||
path_ctx: Option<&PathCompletionCtx>,
|
||||
variant: hir::Variant,
|
||||
local_name: Option<Name>,
|
||||
path: Option<&hir::ModPath>,
|
||||
|
@ -58,9 +59,23 @@ pub(crate) fn render_variant_pat(
|
|||
(name.to_smol_str(), name.escaped().to_smol_str())
|
||||
}
|
||||
};
|
||||
let kind = variant.kind(ctx.db());
|
||||
let label = format_literal_label(name.as_str(), kind);
|
||||
let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?;
|
||||
|
||||
let (label, pat) = match path_ctx {
|
||||
Some(PathCompletionCtx { has_call_parens: true, .. }) => (name, escaped_name.to_string()),
|
||||
_ => {
|
||||
let kind = variant.kind(ctx.db());
|
||||
let label = format_literal_label(name.as_str(), kind);
|
||||
let pat = render_pat(
|
||||
&ctx,
|
||||
pattern_ctx,
|
||||
&escaped_name,
|
||||
kind,
|
||||
&visible_fields,
|
||||
fields_omitted,
|
||||
)?;
|
||||
(label, pat)
|
||||
}
|
||||
};
|
||||
|
||||
Some(build_completion(ctx, label, pat, variant))
|
||||
}
|
||||
|
|
|
@ -23,8 +23,6 @@ mod type_pos;
|
|||
mod use_tree;
|
||||
mod visibility;
|
||||
|
||||
use std::mem;
|
||||
|
||||
use hir::{db::DefDatabase, PrefixKind, Semantics};
|
||||
use ide_db::{
|
||||
base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
|
||||
|
@ -107,12 +105,9 @@ fn completion_list_with_config(
|
|||
) -> String {
|
||||
// filter out all but one builtintype completion for smaller test outputs
|
||||
let items = get_all_items(config, ra_fixture, trigger_character);
|
||||
let mut bt_seen = false;
|
||||
let items = items
|
||||
.into_iter()
|
||||
.filter(|it| {
|
||||
it.kind() != CompletionItemKind::BuiltinType || !mem::replace(&mut bt_seen, true)
|
||||
})
|
||||
.filter(|it| it.kind() != CompletionItemKind::BuiltinType || it.label() == "u32")
|
||||
.filter(|it| include_keywords || it.kind() != CompletionItemKind::Keyword)
|
||||
.filter(|it| include_keywords || it.kind() != CompletionItemKind::Snippet)
|
||||
.sorted_by_key(|it| (it.kind(), it.label().to_owned(), it.detail().map(ToOwned::to_owned)))
|
||||
|
|
|
@ -44,7 +44,6 @@ fn baz() {
|
|||
st Record
|
||||
st Tuple
|
||||
st Unit
|
||||
tt Trait
|
||||
un Union
|
||||
ev TupleV(…) TupleV(u32)
|
||||
bt u32
|
||||
|
@ -137,7 +136,6 @@ impl Unit {
|
|||
st Record
|
||||
st Tuple
|
||||
st Unit
|
||||
tt Trait
|
||||
tp TypeParam
|
||||
un Union
|
||||
ev TupleV(…) TupleV(u32)
|
||||
|
@ -653,3 +651,22 @@ fn main() {
|
|||
"]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complete_record_expr_path() {
|
||||
check(
|
||||
r#"
|
||||
struct Zulu;
|
||||
impl Zulu {
|
||||
fn test() -> Self { }
|
||||
}
|
||||
fn boi(val: Zulu) { }
|
||||
fn main() {
|
||||
boi(Zulu:: $0 {});
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn test() fn() -> Zulu
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -443,7 +443,7 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
bn TupleVariant(…) TupleVariant($1)$0
|
||||
bn TupleVariant TupleVariant
|
||||
"#]],
|
||||
);
|
||||
check_empty(
|
||||
|
@ -458,7 +458,7 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
bn RecordVariant {…} RecordVariant { field$1 }$0
|
||||
bn RecordVariant RecordVariant
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -167,7 +167,6 @@ fn main() {
|
|||
st Foo
|
||||
st Foo {…} Foo { foo1: u32, foo2: u32 }
|
||||
tt Default
|
||||
tt Sized
|
||||
bt u32
|
||||
kw crate::
|
||||
kw self::
|
||||
|
|
|
@ -674,7 +674,60 @@ fn bar() -> Bar {
|
|||
expect![[r#"
|
||||
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]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue