mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-31 23:38:45 +00:00
Auto merge of #12570 - Veykril:completion, r=Veykril
Only run completion functions if their corresponding context is active
This commit is contained in:
commit
09ac44c078
29 changed files with 603 additions and 456 deletions
|
@ -18,7 +18,7 @@ use syntax::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::module_or_attr,
|
completions::module_or_attr,
|
||||||
context::{CompletionContext, IdentContext, PathCompletionCtx, PathKind, Qualified},
|
context::{CompletionContext, PathCompletionCtx, PathKind, Qualified},
|
||||||
item::CompletionItem,
|
item::CompletionItem,
|
||||||
Completions,
|
Completions,
|
||||||
};
|
};
|
||||||
|
@ -34,11 +34,9 @@ pub(crate) use self::derive::complete_derive;
|
||||||
pub(crate) fn complete_known_attribute_input(
|
pub(crate) fn complete_known_attribute_input(
|
||||||
acc: &mut Completions,
|
acc: &mut Completions,
|
||||||
ctx: &CompletionContext,
|
ctx: &CompletionContext,
|
||||||
|
fake_attribute_under_caret: &ast::Attr,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let attribute = match &ctx.ident_ctx {
|
let attribute = fake_attribute_under_caret;
|
||||||
IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: Some(it) } => it,
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
let name_ref = match attribute.path() {
|
let name_ref = match attribute.path() {
|
||||||
Some(p) => Some(p.as_single_name_ref()?),
|
Some(p) => Some(p.as_single_name_ref()?),
|
||||||
None => None,
|
None => None,
|
||||||
|
@ -71,27 +69,30 @@ pub(crate) fn complete_known_attribute_input(
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_attribute(
|
||||||
let (qualified, is_inner, annotated_item_kind) = match ctx.path_context() {
|
acc: &mut Completions,
|
||||||
Some(&PathCompletionCtx {
|
ctx: &CompletionContext,
|
||||||
|
path_ctx: &PathCompletionCtx,
|
||||||
|
) {
|
||||||
|
let (qualified, is_inner, annotated_item_kind) = match path_ctx {
|
||||||
|
&PathCompletionCtx {
|
||||||
kind: PathKind::Attr { kind, annotated_item_kind },
|
kind: PathKind::Attr { kind, annotated_item_kind },
|
||||||
ref qualified,
|
ref qualified,
|
||||||
..
|
..
|
||||||
}) => (qualified, kind == AttrKind::Inner, annotated_item_kind),
|
} => (qualified, kind == AttrKind::Inner, annotated_item_kind),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
match qualified {
|
match qualified {
|
||||||
Qualified::With { resolution, is_super_chain, .. } => {
|
Qualified::With {
|
||||||
|
resolution: Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))),
|
||||||
|
is_super_chain,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
if *is_super_chain {
|
if *is_super_chain {
|
||||||
acc.add_keyword(ctx, "super::");
|
acc.add_keyword(ctx, "super::");
|
||||||
}
|
}
|
||||||
|
|
||||||
let module = match resolution {
|
|
||||||
Some(hir::PathResolution::Def(hir::ModuleDef::Module(it))) => it,
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
|
for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
|
||||||
if let Some(def) = module_or_attr(ctx.db, def) {
|
if let Some(def) = module_or_attr(ctx.db, def) {
|
||||||
acc.add_resolution(ctx, name, def);
|
acc.add_resolution(ctx, name, def);
|
||||||
|
@ -110,7 +111,7 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
});
|
});
|
||||||
acc.add_nameref_keywords_with_colon(ctx);
|
acc.add_nameref_keywords_with_colon(ctx);
|
||||||
}
|
}
|
||||||
Qualified::Infer => {}
|
Qualified::Infer | Qualified::With { .. } => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let attributes = annotated_item_kind.and_then(|kind| {
|
let attributes = annotated_item_kind.and_then(|kind| {
|
||||||
|
|
|
@ -10,27 +10,30 @@ use crate::{
|
||||||
Completions,
|
Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_derive(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_derive(
|
||||||
let (qualified, existing_derives) = match ctx.path_context() {
|
acc: &mut Completions,
|
||||||
Some(PathCompletionCtx {
|
ctx: &CompletionContext,
|
||||||
kind: PathKind::Derive { existing_derives }, qualified, ..
|
path_ctx: &PathCompletionCtx,
|
||||||
}) => (qualified, existing_derives),
|
) {
|
||||||
|
let (qualified, existing_derives) = match path_ctx {
|
||||||
|
PathCompletionCtx { kind: PathKind::Derive { existing_derives }, qualified, .. } => {
|
||||||
|
(qualified, existing_derives)
|
||||||
|
}
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let core = ctx.famous_defs().core();
|
let core = ctx.famous_defs().core();
|
||||||
|
|
||||||
match qualified {
|
match qualified {
|
||||||
Qualified::With { resolution, is_super_chain, .. } => {
|
Qualified::With {
|
||||||
|
resolution: Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))),
|
||||||
|
is_super_chain,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
if *is_super_chain {
|
if *is_super_chain {
|
||||||
acc.add_keyword(ctx, "super::");
|
acc.add_keyword(ctx, "super::");
|
||||||
}
|
}
|
||||||
|
|
||||||
let module = match resolution {
|
|
||||||
Some(hir::PathResolution::Def(hir::ModuleDef::Module(it))) => it,
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
|
for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
|
||||||
let add_def = match def {
|
let add_def = match def {
|
||||||
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
|
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
|
||||||
|
@ -101,7 +104,7 @@ pub(crate) fn complete_derive(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
});
|
});
|
||||||
acc.add_nameref_keywords_with_colon(ctx);
|
acc.add_nameref_keywords_with_colon(ctx);
|
||||||
}
|
}
|
||||||
Qualified::Infer => {}
|
Qualified::Infer | Qualified::With { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,21 +4,16 @@ use ide_db::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{
|
context::{
|
||||||
CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind,
|
CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind, Qualified,
|
||||||
PathCompletionCtx, PathKind, Qualified,
|
|
||||||
},
|
},
|
||||||
CompletionItem, CompletionItemKind, Completions,
|
CompletionItem, CompletionItemKind, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Complete dot accesses, i.e. fields or methods.
|
/// Complete dot accesses, i.e. fields or methods.
|
||||||
pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext, dot_access: &DotAccess) {
|
||||||
let (dot_access, receiver_ty) = match ctx.nameref_ctx() {
|
let receiver_ty = match dot_access {
|
||||||
Some(NameRefContext {
|
DotAccess { receiver_ty: Some(receiver_ty), .. } => &receiver_ty.original,
|
||||||
kind:
|
_ => return,
|
||||||
Some(NameRefKind::DotAccess(access @ DotAccess { receiver_ty: Some(receiver_ty), .. })),
|
|
||||||
..
|
|
||||||
}) => (access, &receiver_ty.original),
|
|
||||||
_ => return complete_undotted_self(acc, ctx),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Suggest .await syntax for types that implement Future trait
|
// Suggest .await syntax for types that implement Future trait
|
||||||
|
@ -43,18 +38,17 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None));
|
complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_undotted_self(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
path_ctx: &PathCompletionCtx,
|
||||||
|
) {
|
||||||
if !ctx.config.enable_self_on_the_fly {
|
if !ctx.config.enable_self_on_the_fly {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match ctx.path_context() {
|
match path_ctx {
|
||||||
Some(
|
PathCompletionCtx { qualified: Qualified::No, kind: PathKind::Expr { .. }, .. }
|
||||||
path_ctx @ PathCompletionCtx {
|
if path_ctx.is_trivial_path() && ctx.qualifier_ctx.none() => {}
|
||||||
qualified: Qualified::No,
|
|
||||||
kind: PathKind::Expr { .. },
|
|
||||||
..
|
|
||||||
},
|
|
||||||
) if path_ctx.is_trivial_path() && ctx.qualifier_ctx.none() => {}
|
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,11 @@ use crate::{
|
||||||
CompletionContext, Completions,
|
CompletionContext, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_expr_path(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
name_ref_ctx: &NameRefContext,
|
||||||
|
) {
|
||||||
let _p = profile::span("complete_expr_path");
|
let _p = profile::span("complete_expr_path");
|
||||||
|
|
||||||
let (
|
let (
|
||||||
|
@ -19,8 +23,8 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
after_if_expr,
|
after_if_expr,
|
||||||
wants_mut_token,
|
wants_mut_token,
|
||||||
in_condition,
|
in_condition,
|
||||||
) = match ctx.nameref_ctx() {
|
) = match name_ref_ctx {
|
||||||
Some(&NameRefContext {
|
&NameRefContext {
|
||||||
kind:
|
kind:
|
||||||
Some(NameRefKind::Path(PathCompletionCtx {
|
Some(NameRefKind::Path(PathCompletionCtx {
|
||||||
kind:
|
kind:
|
||||||
|
@ -36,7 +40,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
..
|
..
|
||||||
})),
|
})),
|
||||||
..
|
..
|
||||||
}) if ctx.qualifier_ctx.none() => (
|
} if ctx.qualifier_ctx.none() => (
|
||||||
qualified,
|
qualified,
|
||||||
in_block_expr,
|
in_block_expr,
|
||||||
in_loop_body,
|
in_loop_body,
|
||||||
|
@ -65,11 +69,8 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
.into_iter()
|
.into_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, ctx, item)),
|
.for_each(|item| add_assoc_item(acc, ctx, item)),
|
||||||
Qualified::With { resolution, .. } => {
|
Qualified::With { resolution: None, .. } => {}
|
||||||
let resolution = match resolution {
|
Qualified::With { resolution: Some(resolution), .. } => {
|
||||||
Some(it) => it,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
// Add associated types on type parameters and `Self`.
|
// Add associated types on type parameters and `Self`.
|
||||||
ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| {
|
ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| {
|
||||||
acc.add_type_alias(ctx, alias);
|
acc.add_type_alias(ctx, alias);
|
||||||
|
|
|
@ -5,9 +5,7 @@ use syntax::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::Completions,
|
completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind,
|
||||||
context::{CompletionContext, IdentContext},
|
|
||||||
CompletionItem, CompletionItemKind,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Most of these are feature gated, we should filter/add feature gate completions once we have them.
|
// Most of these are feature gated, we should filter/add feature gate completions once we have them.
|
||||||
|
@ -42,15 +40,15 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[
|
||||||
"unadjusted",
|
"unadjusted",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub(crate) fn complete_extern_abi(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
pub(crate) fn complete_extern_abi(
|
||||||
let abi_str = match &ctx.ident_ctx {
|
acc: &mut Completions,
|
||||||
IdentContext::String { expanded: Some(expanded), .. }
|
_ctx: &CompletionContext,
|
||||||
if expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) =>
|
expanded: &ast::String,
|
||||||
{
|
) -> Option<()> {
|
||||||
expanded
|
if !expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) {
|
||||||
}
|
return None;
|
||||||
_ => return None,
|
}
|
||||||
};
|
let abi_str = expanded;
|
||||||
let source_range = abi_str.text_range_between_quotes()?;
|
let source_range = abi_str.text_range_between_quotes()?;
|
||||||
for &abi in SUPPORTED_CALLING_CONVENTIONS {
|
for &abi in SUPPORTED_CALLING_CONVENTIONS {
|
||||||
CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc);
|
CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc);
|
||||||
|
|
|
@ -2,16 +2,19 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{
|
context::{
|
||||||
IdentContext, NameContext, NameKind, NameRefContext, NameRefKind, PathCompletionCtx,
|
NameContext, NameKind, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, Qualified,
|
||||||
PathKind, Qualified, TypeLocation,
|
TypeLocation,
|
||||||
},
|
},
|
||||||
CompletionContext, Completions,
|
CompletionContext, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_field_list_tuple_variant(
|
||||||
match &ctx.ident_ctx {
|
acc: &mut Completions,
|
||||||
IdentContext::Name(NameContext { kind: NameKind::RecordField, .. })
|
ctx: &CompletionContext,
|
||||||
| IdentContext::NameRef(NameRefContext {
|
name_ref_ctx: &NameRefContext,
|
||||||
|
) {
|
||||||
|
match name_ref_ctx {
|
||||||
|
NameRefContext {
|
||||||
kind:
|
kind:
|
||||||
Some(NameRefKind::Path(PathCompletionCtx {
|
Some(NameRefKind::Path(PathCompletionCtx {
|
||||||
has_macro_bang: false,
|
has_macro_bang: false,
|
||||||
|
@ -22,7 +25,7 @@ pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext
|
||||||
..
|
..
|
||||||
})),
|
})),
|
||||||
..
|
..
|
||||||
}) => {
|
} => {
|
||||||
if ctx.qualifier_ctx.vis_node.is_none() {
|
if ctx.qualifier_ctx.vis_node.is_none() {
|
||||||
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
|
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
|
||||||
add_keyword("pub(crate)", "pub(crate)");
|
add_keyword("pub(crate)", "pub(crate)");
|
||||||
|
@ -30,6 +33,21 @@ pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext
|
||||||
add_keyword("pub", "pub");
|
add_keyword("pub", "pub");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => return,
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn complete_field_list_record_variant(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
name_ctx: &NameContext,
|
||||||
|
) {
|
||||||
|
if let NameContext { kind: NameKind::RecordField, .. } = name_ctx {
|
||||||
|
if ctx.qualifier_ctx.vis_node.is_none() {
|
||||||
|
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
|
||||||
|
add_keyword("pub(crate)", "pub(crate)");
|
||||||
|
add_keyword("pub(super)", "pub(super)");
|
||||||
|
add_keyword("pub", "pub");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,13 @@ use crate::{
|
||||||
/// `spam: &mut Spam` insert text/label will be suggested.
|
/// `spam: &mut Spam` insert text/label will be suggested.
|
||||||
///
|
///
|
||||||
/// Also complete parameters for closure or local functions from the surrounding defined locals.
|
/// Also complete parameters for closure or local functions from the surrounding defined locals.
|
||||||
pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
pub(crate) fn complete_fn_param(
|
||||||
let (param_list, _, param_kind) = match &ctx.pattern_ctx {
|
acc: &mut Completions,
|
||||||
Some(PatternContext { param_ctx: Some(kind), .. }) => kind,
|
ctx: &CompletionContext,
|
||||||
|
pattern_ctx: &PatternContext,
|
||||||
|
) -> Option<()> {
|
||||||
|
let (param_list, _, param_kind) = match pattern_ctx {
|
||||||
|
PatternContext { param_ctx: Some(kind), .. } => kind,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,28 +2,25 @@
|
||||||
|
|
||||||
use ide_db::syntax_helpers::format_string::is_format_string;
|
use ide_db::syntax_helpers::format_string::is_format_string;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{AstToken, TextRange, TextSize};
|
use syntax::{ast, AstToken, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, Completions};
|
||||||
context::{CompletionContext, IdentContext},
|
|
||||||
CompletionItem, CompletionItemKind, Completions,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Complete identifiers in format strings.
|
/// Complete identifiers in format strings.
|
||||||
pub(crate) fn format_string(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn format_string(
|
||||||
let string = match &ctx.ident_ctx {
|
acc: &mut Completions,
|
||||||
IdentContext::String { expanded: Some(expanded), original }
|
ctx: &CompletionContext,
|
||||||
if is_format_string(&expanded) =>
|
original: &ast::String,
|
||||||
{
|
expanded: &ast::String,
|
||||||
original
|
) {
|
||||||
}
|
if !is_format_string(&expanded) {
|
||||||
_ => return,
|
return;
|
||||||
};
|
}
|
||||||
let cursor = ctx.position.offset;
|
let cursor = ctx.position.offset;
|
||||||
let lit_start = ctx.original_token.text_range().start();
|
let lit_start = ctx.original_token.text_range().start();
|
||||||
let cursor_in_lit = cursor - lit_start;
|
let cursor_in_lit = cursor - lit_start;
|
||||||
|
|
||||||
let prefix = &string.text()[..cursor_in_lit.into()];
|
let prefix = &original.text()[..cursor_in_lit.into()];
|
||||||
let braces = prefix.char_indices().rev().skip_while(|&(_, c)| c.is_alphanumeric()).next_tuple();
|
let braces = prefix.char_indices().rev().skip_while(|&(_, c)| c.is_alphanumeric()).next_tuple();
|
||||||
let brace_offset = match braces {
|
let brace_offset = match braces {
|
||||||
// escaped brace
|
// escaped brace
|
||||||
|
|
|
@ -2,54 +2,63 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::module_or_fn_macro,
|
completions::module_or_fn_macro,
|
||||||
context::{ItemListKind, PathCompletionCtx, PathKind, Qualified},
|
context::{ItemListKind, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, Qualified},
|
||||||
CompletionContext, Completions,
|
CompletionContext, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod trait_impl;
|
pub(crate) mod trait_impl;
|
||||||
|
|
||||||
pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_item_list(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
name_ref_ctx: &NameRefContext,
|
||||||
|
) {
|
||||||
let _p = profile::span("complete_item_list");
|
let _p = profile::span("complete_item_list");
|
||||||
|
|
||||||
if let Some(_) = ctx.name_ctx() {
|
let (qualified, item_list_kind, is_trivial_path) = match name_ref_ctx {
|
||||||
trait_impl::complete_trait_impl(acc, ctx);
|
NameRefContext {
|
||||||
return;
|
kind:
|
||||||
}
|
Some(NameRefKind::Path(
|
||||||
|
ctx @ PathCompletionCtx { kind: PathKind::Item { kind }, qualified, .. },
|
||||||
let (qualified, kind, is_trivial_path) = match ctx.path_context() {
|
)),
|
||||||
Some(ctx @ PathCompletionCtx { kind: PathKind::Item { kind }, qualified, .. }) => {
|
..
|
||||||
(qualified, Some(kind), ctx.is_trivial_path())
|
} => (qualified, Some(kind), ctx.is_trivial_path()),
|
||||||
}
|
NameRefContext {
|
||||||
Some(
|
kind:
|
||||||
ctx @ PathCompletionCtx {
|
Some(NameRefKind::Path(
|
||||||
kind: PathKind::Expr { in_block_expr: true, .. },
|
ctx @ PathCompletionCtx {
|
||||||
qualified,
|
kind: PathKind::Expr { in_block_expr: true, .. },
|
||||||
..
|
qualified,
|
||||||
},
|
..
|
||||||
) => (qualified, None, ctx.is_trivial_path()),
|
},
|
||||||
|
)),
|
||||||
|
..
|
||||||
|
} => (qualified, None, ctx.is_trivial_path()),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches!(kind, Some(ItemListKind::TraitImpl)) {
|
if matches!(item_list_kind, Some(ItemListKind::TraitImpl)) {
|
||||||
trait_impl::complete_trait_impl(acc, ctx);
|
trait_impl::complete_trait_impl_name_ref(acc, ctx, name_ref_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_trivial_path {
|
if is_trivial_path {
|
||||||
add_keywords(acc, ctx, kind);
|
add_keywords(acc, ctx, item_list_kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
if kind.is_none() {
|
if item_list_kind.is_none() {
|
||||||
// this is already handled by expression
|
// this is already handled by expression
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match qualified {
|
match qualified {
|
||||||
Qualified::With { resolution, is_super_chain, .. } => {
|
Qualified::With {
|
||||||
if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = resolution {
|
resolution: Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))),
|
||||||
for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
|
is_super_chain,
|
||||||
if let Some(def) = module_or_fn_macro(ctx.db, def) {
|
..
|
||||||
acc.add_resolution(ctx, name, def);
|
} => {
|
||||||
}
|
for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
|
||||||
|
if let Some(def) = module_or_fn_macro(ctx.db, def) {
|
||||||
|
acc.add_resolution(ctx, name, def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +75,7 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
});
|
});
|
||||||
acc.add_nameref_keywords_with_colon(ctx);
|
acc.add_nameref_keywords_with_colon(ctx);
|
||||||
}
|
}
|
||||||
Qualified::Infer | Qualified::No => {}
|
Qualified::Infer | Qualified::No | Qualified::With { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,8 +44,8 @@ use text_edit::TextEdit;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{
|
context::{
|
||||||
IdentContext, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind,
|
ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, PathCompletionCtx,
|
||||||
PathCompletionCtx, PathKind,
|
PathKind,
|
||||||
},
|
},
|
||||||
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
|
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
|
||||||
};
|
};
|
||||||
|
@ -58,53 +58,41 @@ enum ImplCompletionKind {
|
||||||
Const,
|
Const,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_trait_impl_name(
|
||||||
if let Some((kind, replacement_range, impl_def)) = completion_match(ctx) {
|
acc: &mut Completions,
|
||||||
if let Some(hir_impl) = ctx.sema.to_def(&impl_def) {
|
ctx: &CompletionContext,
|
||||||
get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| {
|
NameContext { name, kind, .. }: &NameContext,
|
||||||
use self::ImplCompletionKind::*;
|
) -> Option<()> {
|
||||||
match (item, kind) {
|
let kind = match kind {
|
||||||
(hir::AssocItem::Function(func), All | Fn) => {
|
NameKind::Const => ImplCompletionKind::Const,
|
||||||
add_function_impl(acc, ctx, replacement_range, func, hir_impl)
|
NameKind::Function => ImplCompletionKind::Fn,
|
||||||
}
|
NameKind::TypeAlias => ImplCompletionKind::TypeAlias,
|
||||||
(hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
|
_ => return None,
|
||||||
add_type_alias_impl(acc, ctx, replacement_range, type_alias)
|
};
|
||||||
}
|
let token = ctx.token.clone();
|
||||||
(hir::AssocItem::Const(const_), All | Const) => {
|
let item = match name {
|
||||||
add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
|
Some(name) => name.syntax().parent(),
|
||||||
}
|
None => if token.kind() == SyntaxKind::WHITESPACE { token.prev_token()? } else { token }
|
||||||
_ => {}
|
.parent(),
|
||||||
}
|
}?;
|
||||||
});
|
complete_trait_impl(
|
||||||
}
|
acc,
|
||||||
}
|
ctx,
|
||||||
|
kind,
|
||||||
|
replacement_range(ctx, &item),
|
||||||
|
// item -> ASSOC_ITEM_LIST -> IMPL
|
||||||
|
ast::Impl::cast(item.parent()?.parent()?)?,
|
||||||
|
);
|
||||||
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, TextRange, ast::Impl)> {
|
pub(crate) fn complete_trait_impl_name_ref(
|
||||||
match &ctx.ident_ctx {
|
acc: &mut Completions,
|
||||||
IdentContext::Name(NameContext { name, kind, .. }) => {
|
ctx: &CompletionContext,
|
||||||
let kind = match kind {
|
name_ref_ctx: &NameRefContext,
|
||||||
NameKind::Const => ImplCompletionKind::Const,
|
) -> Option<()> {
|
||||||
NameKind::Function => ImplCompletionKind::Fn,
|
match name_ref_ctx {
|
||||||
NameKind::TypeAlias => ImplCompletionKind::TypeAlias,
|
NameRefContext {
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
let token = ctx.token.clone();
|
|
||||||
let item = match name {
|
|
||||||
Some(name) => name.syntax().parent(),
|
|
||||||
None => {
|
|
||||||
if token.kind() == SyntaxKind::WHITESPACE { token.prev_token()? } else { token }
|
|
||||||
.parent()
|
|
||||||
}
|
|
||||||
}?;
|
|
||||||
Some((
|
|
||||||
kind,
|
|
||||||
replacement_range(ctx, &item),
|
|
||||||
// item -> ASSOC_ITEM_LIST -> IMPL
|
|
||||||
ast::Impl::cast(item.parent()?.parent()?)?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
IdentContext::NameRef(NameRefContext {
|
|
||||||
nameref,
|
nameref,
|
||||||
kind:
|
kind:
|
||||||
Some(NameRefKind::Path(
|
Some(NameRefKind::Path(
|
||||||
|
@ -113,15 +101,44 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Text
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
}) if path_ctx.is_trivial_path() => Some((
|
} if path_ctx.is_trivial_path() => complete_trait_impl(
|
||||||
|
acc,
|
||||||
|
ctx,
|
||||||
ImplCompletionKind::All,
|
ImplCompletionKind::All,
|
||||||
match nameref {
|
match nameref {
|
||||||
Some(name) => name.syntax().text_range(),
|
Some(name) => name.syntax().text_range(),
|
||||||
None => ctx.source_range(),
|
None => ctx.source_range(),
|
||||||
},
|
},
|
||||||
ctx.impl_def.clone()?,
|
ctx.impl_def.clone()?,
|
||||||
)),
|
),
|
||||||
_ => None,
|
_ => (),
|
||||||
|
}
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete_trait_impl(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
kind: ImplCompletionKind,
|
||||||
|
replacement_range: TextRange,
|
||||||
|
impl_def: ast::Impl,
|
||||||
|
) {
|
||||||
|
if let Some(hir_impl) = ctx.sema.to_def(&impl_def) {
|
||||||
|
get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| {
|
||||||
|
use self::ImplCompletionKind::*;
|
||||||
|
match (item, kind) {
|
||||||
|
(hir::AssocItem::Function(func), All | Fn) => {
|
||||||
|
add_function_impl(acc, ctx, replacement_range, func, hir_impl)
|
||||||
|
}
|
||||||
|
(hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
|
||||||
|
add_type_alias_impl(acc, ctx, replacement_range, type_alias)
|
||||||
|
}
|
||||||
|
(hir::AssocItem::Const(const_), All | Const) => {
|
||||||
|
add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,17 @@
|
||||||
//! Completes keywords, except:
|
//! Completes `where` and `for` keywords.
|
||||||
//! - `self`, `super` and `crate`, as these are considered part of path completions.
|
|
||||||
//! - `await`, as this is a postfix completion we handle this in the postfix completions.
|
|
||||||
|
|
||||||
use syntax::ast::Item;
|
use syntax::ast::{self, Item};
|
||||||
|
|
||||||
use crate::{
|
use crate::{CompletionContext, Completions};
|
||||||
context::{NameRefContext, NameRefKind},
|
|
||||||
CompletionContext, Completions,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
|
|
||||||
let item = match ctx.nameref_ctx() {
|
|
||||||
Some(NameRefContext { kind: Some(NameRefKind::Keyword(item)), .. }) => item,
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
pub(crate) fn complete_special_keywords(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
keyword_item: &ast::Item,
|
||||||
|
) {
|
||||||
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
|
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
|
||||||
|
|
||||||
match item {
|
match keyword_item {
|
||||||
Item::Impl(it) => {
|
Item::Impl(it) => {
|
||||||
if it.for_token().is_none() && it.trait_().is_none() && it.self_ty().is_some() {
|
if it.for_token().is_none() && it.trait_().is_none() && it.self_ty().is_some() {
|
||||||
add_keyword("for", "for");
|
add_keyword("for", "for");
|
||||||
|
|
|
@ -16,13 +16,17 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Completes lifetimes.
|
/// Completes lifetimes.
|
||||||
pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_lifetime(
|
||||||
let (lp, lifetime) = match ctx.lifetime_ctx() {
|
acc: &mut Completions,
|
||||||
Some(LifetimeContext { kind: LifetimeKind::Lifetime, lifetime }) => (None, lifetime),
|
ctx: &CompletionContext,
|
||||||
Some(LifetimeContext {
|
lifetime_ctx: &LifetimeContext,
|
||||||
|
) {
|
||||||
|
let (lp, lifetime) = match lifetime_ctx {
|
||||||
|
LifetimeContext { kind: LifetimeKind::Lifetime, lifetime } => (None, lifetime),
|
||||||
|
LifetimeContext {
|
||||||
kind: LifetimeKind::LifetimeParam { is_decl: false, param },
|
kind: LifetimeKind::LifetimeParam { is_decl: false, param },
|
||||||
lifetime,
|
lifetime,
|
||||||
}) => (Some(param), lifetime),
|
} => (Some(param), lifetime),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let param_lifetime = match (lifetime, lp.and_then(|lp| lp.lifetime())) {
|
let param_lifetime = match (lifetime, lp.and_then(|lp| lp.lifetime())) {
|
||||||
|
@ -48,8 +52,12 @@ pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Completes labels.
|
/// Completes labels.
|
||||||
pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_label(
|
||||||
if !matches!(ctx.lifetime_ctx(), Some(LifetimeContext { kind: LifetimeKind::LabelRef, .. })) {
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
lifetime_ctx: &LifetimeContext,
|
||||||
|
) {
|
||||||
|
if !matches!(lifetime_ctx, LifetimeContext { kind: LifetimeKind::LabelRef, .. }) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx.process_all_names_raw(&mut |name, res| {
|
ctx.process_all_names_raw(&mut |name, res| {
|
||||||
|
|
|
@ -15,9 +15,13 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Complete mod declaration, i.e. `mod $0;`
|
/// Complete mod declaration, i.e. `mod $0;`
|
||||||
pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
pub(crate) fn complete_mod(
|
||||||
let mod_under_caret = match ctx.name_ctx() {
|
acc: &mut Completions,
|
||||||
Some(NameContext { kind: NameKind::Module(mod_under_caret), .. }) => mod_under_caret,
|
ctx: &CompletionContext,
|
||||||
|
name_ctx: &NameContext,
|
||||||
|
) -> Option<()> {
|
||||||
|
let mod_under_caret = match name_ctx {
|
||||||
|
NameContext { kind: NameKind::Module(mod_under_caret), .. } => mod_under_caret,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
if mod_under_caret.item_list().is_some() {
|
if mod_under_caret.item_list().is_some() {
|
||||||
|
|
|
@ -5,22 +5,16 @@ use ide_db::FxHashSet;
|
||||||
use syntax::ast::Pat;
|
use syntax::ast::Pat;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{PathCompletionCtx, PatternRefutability, Qualified},
|
context::{PathCompletionCtx, PathKind, PatternContext, PatternRefutability, Qualified},
|
||||||
CompletionContext, Completions,
|
CompletionContext, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Completes constants and paths in unqualified patterns.
|
/// Completes constants and paths in unqualified patterns.
|
||||||
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_pattern(
|
||||||
let patctx = match &ctx.pattern_ctx {
|
acc: &mut Completions,
|
||||||
Some(ctx) => ctx,
|
ctx: &CompletionContext,
|
||||||
_ => return,
|
patctx: &PatternContext,
|
||||||
};
|
) {
|
||||||
|
|
||||||
if let Some(path_ctx) = ctx.path_context() {
|
|
||||||
pattern_path_completion(acc, ctx, path_ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match patctx.parent_pat.as_ref() {
|
match patctx.parent_pat.as_ref() {
|
||||||
Some(Pat::RangePat(_) | Pat::BoxPat(_)) => (),
|
Some(Pat::RangePat(_) | Pat::BoxPat(_)) => (),
|
||||||
Some(Pat::RefPat(r)) => {
|
Some(Pat::RefPat(r)) => {
|
||||||
|
@ -108,22 +102,20 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pattern_path_completion(
|
pub(crate) fn pattern_path_completion(
|
||||||
acc: &mut Completions,
|
acc: &mut Completions,
|
||||||
ctx: &CompletionContext,
|
ctx: &CompletionContext,
|
||||||
PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
|
PathCompletionCtx { qualified, kind, .. }: &PathCompletionCtx,
|
||||||
) {
|
) {
|
||||||
|
if !matches!(kind, PathKind::Pat) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
match qualified {
|
match qualified {
|
||||||
Qualified::With { resolution, is_super_chain, .. } => {
|
Qualified::With { resolution: Some(resolution), is_super_chain, .. } => {
|
||||||
if *is_super_chain {
|
if *is_super_chain {
|
||||||
acc.add_keyword(ctx, "super::");
|
acc.add_keyword(ctx, "super::");
|
||||||
}
|
}
|
||||||
|
|
||||||
let resolution = match resolution {
|
|
||||||
Some(it) => it,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
match resolution {
|
match resolution {
|
||||||
hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
|
hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
|
||||||
let module_scope = module.scope(ctx.db, Some(ctx.module));
|
let module_scope = module.scope(ctx.db, Some(ctx.module));
|
||||||
|
@ -208,6 +200,6 @@ fn pattern_path_completion(
|
||||||
|
|
||||||
acc.add_nameref_keywords_with_colon(ctx);
|
acc.add_nameref_keywords_with_colon(ctx);
|
||||||
}
|
}
|
||||||
Qualified::Infer => {}
|
Qualified::Infer | Qualified::With { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,27 +13,22 @@ use text_edit::TextEdit;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::postfix::format_like::add_format_like_completions,
|
completions::postfix::format_like::add_format_like_completions,
|
||||||
context::{CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind},
|
context::{CompletionContext, DotAccess, DotAccessKind},
|
||||||
item::{Builder, CompletionRelevancePostfixMatch},
|
item::{Builder, CompletionRelevancePostfixMatch},
|
||||||
CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope,
|
CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_postfix(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
dot_access: &DotAccess,
|
||||||
|
) {
|
||||||
if !ctx.config.enable_postfix_completions {
|
if !ctx.config.enable_postfix_completions {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (dot_receiver, receiver_ty, receiver_is_ambiguous_float_literal) = match ctx.nameref_ctx() {
|
let (dot_receiver, receiver_ty, receiver_is_ambiguous_float_literal) = match dot_access {
|
||||||
Some(NameRefContext {
|
DotAccess { receiver_ty: Some(ty), receiver: Some(it), kind, .. } => (
|
||||||
kind:
|
|
||||||
Some(NameRefKind::DotAccess(DotAccess {
|
|
||||||
receiver_ty: Some(ty),
|
|
||||||
receiver: Some(it),
|
|
||||||
kind,
|
|
||||||
..
|
|
||||||
})),
|
|
||||||
..
|
|
||||||
}) => (
|
|
||||||
it,
|
it,
|
||||||
&ty.original,
|
&ty.original,
|
||||||
match *kind {
|
match *kind {
|
||||||
|
|
|
@ -1,36 +1,34 @@
|
||||||
//! Complete fields in record literals and patterns.
|
//! Complete fields in record literals and patterns.
|
||||||
use ide_db::SymbolKind;
|
use ide_db::SymbolKind;
|
||||||
use syntax::{ast::Expr, T};
|
use syntax::{
|
||||||
|
ast::{self, Expr},
|
||||||
|
T,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{
|
context::{PathCompletionCtx, PathKind, PatternContext, Qualified},
|
||||||
NameRefContext, NameRefKind, PathCompletionCtx, PathKind, PatternContext, Qualified,
|
|
||||||
},
|
|
||||||
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
|
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
|
||||||
CompletionRelevancePostfixMatch, Completions,
|
CompletionRelevancePostfixMatch, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
pub(crate) fn complete_record_pattern_fields(
|
||||||
let missing_fields = if let Some(PatternContext { record_pat: Some(record_pat), .. }) =
|
acc: &mut Completions,
|
||||||
&ctx.pattern_ctx
|
ctx: &CompletionContext,
|
||||||
{
|
pattern_ctx: &PatternContext,
|
||||||
ctx.sema.record_pattern_missing_fields(record_pat)
|
) {
|
||||||
} else if let Some(NameRefContext {
|
if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx {
|
||||||
kind:
|
complete_fields(acc, ctx, ctx.sema.record_pattern_missing_fields(record_pat));
|
||||||
Some(
|
}
|
||||||
NameRefKind::RecordExpr(record_expr)
|
}
|
||||||
| NameRefKind::Path(PathCompletionCtx {
|
pub(crate) fn complete_record_expr_fields_record_expr(
|
||||||
kind: PathKind::Expr { is_func_update: Some(record_expr), .. },
|
acc: &mut Completions,
|
||||||
qualified: Qualified::No,
|
ctx: &CompletionContext,
|
||||||
..
|
record_expr: &ast::RecordExpr,
|
||||||
}),
|
) {
|
||||||
),
|
let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
|
||||||
..
|
|
||||||
}) = ctx.nameref_ctx()
|
|
||||||
{
|
|
||||||
let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
|
|
||||||
|
|
||||||
if let Some(hir::Adt::Union(un)) = ty.as_ref().and_then(|t| t.original.as_adt()) {
|
let missing_fields = match ty.as_ref().and_then(|t| t.original.as_adt()) {
|
||||||
|
Some(hir::Adt::Union(un)) => {
|
||||||
// ctx.sema.record_literal_missing_fields will always return
|
// ctx.sema.record_literal_missing_fields will always return
|
||||||
// an empty Vec on a union literal. This is normally
|
// an empty Vec on a union literal. This is normally
|
||||||
// reasonable, but here we'd like to present the full list
|
// reasonable, but here we'd like to present the full list
|
||||||
|
@ -40,47 +38,80 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
|
||||||
|
|
||||||
match were_fields_specified {
|
match were_fields_specified {
|
||||||
false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
|
false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
|
||||||
true => vec![],
|
true => return,
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
|
let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
|
||||||
|
|
||||||
let default_trait = ctx.famous_defs().core_default_Default();
|
add_default_update(acc, ctx, ty, &missing_fields);
|
||||||
let impl_default_trait =
|
|
||||||
default_trait.zip(ty.as_ref()).map_or(false, |(default_trait, ty)| {
|
|
||||||
ty.original.impls_trait(ctx.db, default_trait, &[])
|
|
||||||
});
|
|
||||||
|
|
||||||
if impl_default_trait && !missing_fields.is_empty() {
|
|
||||||
let completion_text = "..Default::default()";
|
|
||||||
let mut item =
|
|
||||||
CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
|
|
||||||
let completion_text =
|
|
||||||
completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text);
|
|
||||||
item.insert_text(completion_text).set_relevance(CompletionRelevance {
|
|
||||||
postfix_match: Some(CompletionRelevancePostfixMatch::Exact),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
item.add_to(acc);
|
|
||||||
}
|
|
||||||
if ctx.previous_token_is(T![.]) {
|
if ctx.previous_token_is(T![.]) {
|
||||||
let mut item =
|
let mut item =
|
||||||
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
|
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
|
||||||
item.insert_text(".");
|
item.insert_text(".");
|
||||||
item.add_to(acc);
|
item.add_to(acc);
|
||||||
return None;
|
return;
|
||||||
}
|
}
|
||||||
missing_fields
|
missing_fields
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
|
complete_fields(acc, ctx, missing_fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_default_update(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
ty: Option<hir::TypeInfo>,
|
||||||
|
missing_fields: &[(hir::Field, hir::Type)],
|
||||||
|
) {
|
||||||
|
let default_trait = ctx.famous_defs().core_default_Default();
|
||||||
|
let impl_default_trait = default_trait
|
||||||
|
.zip(ty.as_ref())
|
||||||
|
.map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[]));
|
||||||
|
if impl_default_trait && !missing_fields.is_empty() {
|
||||||
|
let completion_text = "..Default::default()";
|
||||||
|
let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
|
||||||
|
let completion_text =
|
||||||
|
completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text);
|
||||||
|
item.insert_text(completion_text).set_relevance(CompletionRelevance {
|
||||||
|
postfix_match: Some(CompletionRelevancePostfixMatch::Exact),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
item.add_to(acc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn complete_record_expr_func_update(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
path_ctx: &PathCompletionCtx,
|
||||||
|
) {
|
||||||
|
if let PathCompletionCtx {
|
||||||
|
kind: PathKind::Expr { is_func_update: Some(record_expr), .. },
|
||||||
|
qualified: Qualified::No,
|
||||||
|
..
|
||||||
|
} = path_ctx
|
||||||
|
{
|
||||||
|
let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
|
||||||
|
|
||||||
|
match ty.as_ref().and_then(|t| t.original.as_adt()) {
|
||||||
|
Some(hir::Adt::Union(_)) => (),
|
||||||
|
_ => {
|
||||||
|
let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
|
||||||
|
add_default_update(acc, ctx, ty, &missing_fields);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete_fields(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
missing_fields: Vec<(hir::Field, hir::Type)>,
|
||||||
|
) {
|
||||||
for (field, ty) in missing_fields {
|
for (field, ty) in missing_fields {
|
||||||
acc.add_field(ctx, None, field, &ty);
|
acc.add_field(ctx, None, field, &ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -15,13 +15,17 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str)
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_expr_snippet(
|
||||||
let &can_be_stmt = match ctx.path_context() {
|
acc: &mut Completions,
|
||||||
Some(PathCompletionCtx {
|
ctx: &CompletionContext,
|
||||||
|
path_ctx: &PathCompletionCtx,
|
||||||
|
) {
|
||||||
|
let &can_be_stmt = match path_ctx {
|
||||||
|
PathCompletionCtx {
|
||||||
qualified: Qualified::No,
|
qualified: Qualified::No,
|
||||||
kind: PathKind::Expr { in_block_expr, .. },
|
kind: PathKind::Expr { in_block_expr, .. },
|
||||||
..
|
..
|
||||||
}) => in_block_expr,
|
} => in_block_expr,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,13 +44,17 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_item_snippet(
|
||||||
let path_kind = match ctx.path_context() {
|
acc: &mut Completions,
|
||||||
Some(PathCompletionCtx {
|
ctx: &CompletionContext,
|
||||||
|
path_ctx: &PathCompletionCtx,
|
||||||
|
) {
|
||||||
|
let path_kind = match path_ctx {
|
||||||
|
PathCompletionCtx {
|
||||||
qualified: Qualified::No,
|
qualified: Qualified::No,
|
||||||
kind: kind @ (PathKind::Item { .. } | PathKind::Expr { in_block_expr: true, .. }),
|
kind: kind @ (PathKind::Item { .. } | PathKind::Expr { in_block_expr: true, .. }),
|
||||||
..
|
..
|
||||||
}) => kind,
|
} => kind,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
if !ctx.qualifier_ctx.none() {
|
if !ctx.qualifier_ctx.none() {
|
||||||
|
|
|
@ -10,11 +10,15 @@ use crate::{
|
||||||
CompletionContext, Completions,
|
CompletionContext, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_type_path(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
path_ctx: &PathCompletionCtx,
|
||||||
|
) {
|
||||||
let _p = profile::span("complete_type_path");
|
let _p = profile::span("complete_type_path");
|
||||||
|
|
||||||
let (location, qualified) = match ctx.path_context() {
|
let (location, qualified) = match path_ctx {
|
||||||
Some(PathCompletionCtx { kind: PathKind::Type { location }, qualified, .. }) => {
|
PathCompletionCtx { kind: PathKind::Type { location }, qualified, .. } => {
|
||||||
(location, qualified)
|
(location, qualified)
|
||||||
}
|
}
|
||||||
_ => return,
|
_ => return,
|
||||||
|
@ -58,11 +62,8 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
.into_iter()
|
.into_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::With { resolution, .. } => {
|
Qualified::With { resolution: None, .. } => {}
|
||||||
let resolution = match resolution {
|
Qualified::With { resolution: Some(resolution), .. } => {
|
||||||
Some(it) => it,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
// Add associated types on type parameters and `Self`.
|
// Add associated types on type parameters and `Self`.
|
||||||
ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| {
|
ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| {
|
||||||
acc.add_type_alias(ctx, alias);
|
acc.add_type_alias(ctx, alias);
|
||||||
|
@ -87,7 +88,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
|
hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
|
||||||
hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
|
hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
|
||||||
hir::ModuleDef::BuiltinType(builtin) => builtin.ty(ctx.db),
|
hir::ModuleDef::BuiltinType(builtin) => builtin.ty(ctx.db),
|
||||||
_ => unreachable!(),
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
// XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
|
// XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
|
||||||
|
@ -190,14 +191,16 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete_inferred_type(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
pub(crate) fn complete_inferred_type(
|
||||||
let pat = match ctx.path_context() {
|
acc: &mut Completions,
|
||||||
Some(
|
ctx: &CompletionContext,
|
||||||
ctx @ PathCompletionCtx {
|
path_ctx: &PathCompletionCtx,
|
||||||
kind: PathKind::Type { location: TypeLocation::TypeAscription(ascription), .. },
|
) -> Option<()> {
|
||||||
..
|
let pat = match path_ctx {
|
||||||
},
|
PathCompletionCtx {
|
||||||
) if ctx.is_trivial_path() => ascription,
|
kind: PathKind::Type { location: TypeLocation::TypeAscription(ascription), .. },
|
||||||
|
..
|
||||||
|
} if path_ctx.is_trivial_path() => ascription,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
let x = match pat {
|
let x = match pat {
|
||||||
|
|
|
@ -12,9 +12,13 @@ use crate::{
|
||||||
CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
|
CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_use_tree(
|
||||||
let (qualified, name_ref, use_tree_parent) = match ctx.nameref_ctx() {
|
acc: &mut Completions,
|
||||||
Some(NameRefContext {
|
ctx: &CompletionContext,
|
||||||
|
name_ref_ctx: &NameRefContext,
|
||||||
|
) {
|
||||||
|
let (qualified, name_ref, use_tree_parent) = match name_ref_ctx {
|
||||||
|
NameRefContext {
|
||||||
kind:
|
kind:
|
||||||
Some(NameRefKind::Path(PathCompletionCtx {
|
Some(NameRefKind::Path(PathCompletionCtx {
|
||||||
kind: PathKind::Use,
|
kind: PathKind::Use,
|
||||||
|
@ -24,12 +28,12 @@ pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
})),
|
})),
|
||||||
nameref,
|
nameref,
|
||||||
..
|
..
|
||||||
}) => (qualified, nameref, use_tree_parent),
|
} => (qualified, nameref, use_tree_parent),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
match qualified {
|
match qualified {
|
||||||
Qualified::With { path, resolution, is_super_chain } => {
|
Qualified::With { path, resolution: Some(resolution), is_super_chain } => {
|
||||||
if *is_super_chain {
|
if *is_super_chain {
|
||||||
acc.add_keyword(ctx, "super::");
|
acc.add_keyword(ctx, "super::");
|
||||||
}
|
}
|
||||||
|
@ -43,11 +47,6 @@ pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
acc.add_keyword(ctx, "self");
|
acc.add_keyword(ctx, "self");
|
||||||
}
|
}
|
||||||
|
|
||||||
let resolution = match resolution {
|
|
||||||
Some(it) => it,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut already_imported_names = FxHashSet::default();
|
let mut already_imported_names = FxHashSet::default();
|
||||||
if let Some(list) = ctx.token.parent_ancestors().find_map(ast::UseTreeList::cast) {
|
if let Some(list) = ctx.token.parent_ancestors().find_map(ast::UseTreeList::cast) {
|
||||||
let use_tree = list.parent_use_tree();
|
let use_tree = list.parent_use_tree();
|
||||||
|
@ -135,6 +134,6 @@ pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
});
|
});
|
||||||
acc.add_nameref_keywords_with_colon(ctx);
|
acc.add_nameref_keywords_with_colon(ctx);
|
||||||
}
|
}
|
||||||
Qualified::Infer => {}
|
Qualified::Infer | Qualified::With { resolution: None, .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,29 +7,31 @@ use crate::{
|
||||||
Completions,
|
Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_vis_path(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_vis_path(
|
||||||
let (qualified, &has_in_token) = match ctx.path_context() {
|
acc: &mut Completions,
|
||||||
Some(PathCompletionCtx { kind: PathKind::Vis { has_in_token }, qualified, .. }) => {
|
ctx: &CompletionContext,
|
||||||
|
path_ctx: &PathCompletionCtx,
|
||||||
|
) {
|
||||||
|
let (qualified, &has_in_token) = match path_ctx {
|
||||||
|
PathCompletionCtx { kind: PathKind::Vis { has_in_token }, qualified, .. } => {
|
||||||
(qualified, has_in_token)
|
(qualified, has_in_token)
|
||||||
}
|
}
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
match qualified {
|
match qualified {
|
||||||
Qualified::With { resolution, is_super_chain, .. } => {
|
Qualified::With {
|
||||||
|
resolution: Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))),
|
||||||
|
is_super_chain,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// Try completing next child module of the path that is still a parent of the current module
|
// Try completing next child module of the path that is still a parent of the current module
|
||||||
if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = resolution {
|
let next_towards_current =
|
||||||
let next_towards_current = ctx
|
ctx.module.path_to_root(ctx.db).into_iter().take_while(|it| it != module).last();
|
||||||
.module
|
if let Some(next) = next_towards_current {
|
||||||
.path_to_root(ctx.db)
|
if let Some(name) = next.name(ctx.db) {
|
||||||
.into_iter()
|
cov_mark::hit!(visibility_qualified);
|
||||||
.take_while(|it| it != module)
|
acc.add_resolution(ctx, name, ScopeDef::ModuleDef(next.into()));
|
||||||
.last();
|
|
||||||
if let Some(next) = next_towards_current {
|
|
||||||
if let Some(name) = next.name(ctx.db) {
|
|
||||||
cov_mark::hit!(visibility_qualified);
|
|
||||||
acc.add_resolution(ctx, name, ScopeDef::ModuleDef(next.into()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +39,7 @@ pub(crate) fn complete_vis_path(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
acc.add_keyword(ctx, "super::");
|
acc.add_keyword(ctx, "super::");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Qualified::Absolute | Qualified::Infer => {}
|
Qualified::Absolute | Qualified::Infer | 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);
|
||||||
|
|
|
@ -362,6 +362,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
FamousDefs(&self.sema, self.krate)
|
FamousDefs(&self.sema, self.krate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: This shouldn't exist
|
||||||
pub(super) fn nameref_ctx(&self) -> Option<&NameRefContext> {
|
pub(super) fn nameref_ctx(&self) -> Option<&NameRefContext> {
|
||||||
match &self.ident_ctx {
|
match &self.ident_ctx {
|
||||||
IdentContext::NameRef(it) => Some(it),
|
IdentContext::NameRef(it) => Some(it),
|
||||||
|
@ -369,20 +370,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn name_ctx(&self) -> Option<&NameContext> {
|
// FIXME: This shouldn't exist
|
||||||
match &self.ident_ctx {
|
|
||||||
IdentContext::Name(it) => Some(it),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn lifetime_ctx(&self) -> Option<&LifetimeContext> {
|
|
||||||
match &self.ident_ctx {
|
|
||||||
IdentContext::Lifetime(it) => Some(it),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
|
pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
|
||||||
match self.nameref_ctx() {
|
match self.nameref_ctx() {
|
||||||
Some(NameRefContext {
|
Some(NameRefContext {
|
||||||
|
@ -393,10 +381,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn has_dot_receiver(&self) -> bool {
|
// FIXME: This shouldn't exist
|
||||||
self.dot_receiver().is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
|
pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
|
||||||
self.nameref_ctx().and_then(|ctx| match &ctx.kind {
|
self.nameref_ctx().and_then(|ctx| match &ctx.kind {
|
||||||
Some(NameRefKind::Path(path)) => Some(path),
|
Some(NameRefKind::Path(path)) => Some(path),
|
||||||
|
@ -404,6 +389,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: This shouldn't exist
|
||||||
pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
|
pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
|
||||||
self.path_context().and_then(|it| match &it.qualified {
|
self.path_context().and_then(|it| match &it.qualified {
|
||||||
Qualified::With { path, .. } => Some(path),
|
Qualified::With { path, .. } => Some(path),
|
||||||
|
|
|
@ -23,7 +23,14 @@ use ide_db::{
|
||||||
use syntax::algo;
|
use syntax::algo;
|
||||||
use text_edit::TextEdit;
|
use text_edit::TextEdit;
|
||||||
|
|
||||||
use crate::{completions::Completions, context::CompletionContext};
|
use crate::{
|
||||||
|
completions::Completions,
|
||||||
|
context::{
|
||||||
|
CompletionContext,
|
||||||
|
IdentContext::{self, NameRef},
|
||||||
|
NameRefContext, NameRefKind,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
config::{CallableSnippets, CompletionConfig},
|
config::{CallableSnippets, CompletionConfig},
|
||||||
|
@ -145,40 +152,94 @@ pub fn completions(
|
||||||
trigger_character: Option<char>,
|
trigger_character: Option<char>,
|
||||||
) -> Option<Completions> {
|
) -> Option<Completions> {
|
||||||
let ctx = &CompletionContext::new(db, position, config)?;
|
let ctx = &CompletionContext::new(db, position, config)?;
|
||||||
let mut acc = Completions::default();
|
let mut completions = Completions::default();
|
||||||
|
|
||||||
{
|
// prevent `(` from triggering unwanted completion noise
|
||||||
let acc = &mut acc;
|
if trigger_character == Some('(') {
|
||||||
// prevent `(` from triggering unwanted completion noise
|
if let NameRef(NameRefContext { kind: Some(NameRefKind::Path(path_ctx)), .. }) =
|
||||||
if trigger_character != Some('(') {
|
&ctx.ident_ctx
|
||||||
completions::attribute::complete_attribute(acc, ctx);
|
{
|
||||||
completions::attribute::complete_derive(acc, ctx);
|
completions::vis::complete_vis_path(&mut completions, ctx, path_ctx);
|
||||||
completions::attribute::complete_known_attribute_input(acc, ctx);
|
|
||||||
completions::dot::complete_dot(acc, ctx);
|
|
||||||
completions::expr::complete_expr_path(acc, ctx);
|
|
||||||
completions::extern_abi::complete_extern_abi(acc, ctx);
|
|
||||||
completions::field::complete_field_list(acc, ctx);
|
|
||||||
completions::flyimport::import_on_the_fly(acc, ctx);
|
|
||||||
completions::fn_param::complete_fn_param(acc, ctx);
|
|
||||||
completions::format_string::format_string(acc, ctx);
|
|
||||||
completions::item_list::complete_item_list(acc, ctx);
|
|
||||||
completions::keyword::complete_expr_keyword(acc, ctx);
|
|
||||||
completions::lifetime::complete_label(acc, ctx);
|
|
||||||
completions::lifetime::complete_lifetime(acc, ctx);
|
|
||||||
completions::mod_::complete_mod(acc, ctx);
|
|
||||||
completions::pattern::complete_pattern(acc, ctx);
|
|
||||||
completions::postfix::complete_postfix(acc, ctx);
|
|
||||||
completions::record::complete_record(acc, ctx);
|
|
||||||
completions::snippet::complete_expr_snippet(acc, ctx);
|
|
||||||
completions::snippet::complete_item_snippet(acc, ctx);
|
|
||||||
completions::r#type::complete_type_path(acc, ctx);
|
|
||||||
completions::r#type::complete_inferred_type(acc, ctx);
|
|
||||||
completions::use_::complete_use_tree(acc, ctx);
|
|
||||||
}
|
}
|
||||||
completions::vis::complete_vis_path(acc, ctx);
|
// prevent `(` from triggering unwanted completion noise
|
||||||
|
return Some(completions);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(acc)
|
{
|
||||||
|
let acc = &mut completions;
|
||||||
|
|
||||||
|
match &ctx.ident_ctx {
|
||||||
|
IdentContext::Name(name_ctx) => {
|
||||||
|
completions::field::complete_field_list_record_variant(acc, ctx, name_ctx);
|
||||||
|
completions::mod_::complete_mod(acc, ctx, name_ctx);
|
||||||
|
completions::item_list::trait_impl::complete_trait_impl_name(acc, ctx, name_ctx);
|
||||||
|
}
|
||||||
|
NameRef(name_ref_ctx @ NameRefContext { kind, .. }) => {
|
||||||
|
completions::expr::complete_expr_path(acc, ctx, name_ref_ctx);
|
||||||
|
completions::field::complete_field_list_tuple_variant(acc, ctx, name_ref_ctx);
|
||||||
|
completions::use_::complete_use_tree(acc, ctx, name_ref_ctx);
|
||||||
|
completions::item_list::complete_item_list(acc, ctx, name_ref_ctx);
|
||||||
|
match kind {
|
||||||
|
Some(NameRefKind::Path(path_ctx)) => {
|
||||||
|
completions::record::complete_record_expr_func_update(acc, ctx, path_ctx);
|
||||||
|
completions::attribute::complete_attribute(acc, ctx, path_ctx);
|
||||||
|
completions::attribute::complete_derive(acc, ctx, path_ctx);
|
||||||
|
completions::dot::complete_undotted_self(acc, ctx, path_ctx);
|
||||||
|
completions::pattern::pattern_path_completion(acc, ctx, path_ctx);
|
||||||
|
completions::r#type::complete_inferred_type(acc, ctx, path_ctx);
|
||||||
|
completions::r#type::complete_type_path(acc, ctx, path_ctx);
|
||||||
|
completions::snippet::complete_expr_snippet(acc, ctx, path_ctx);
|
||||||
|
completions::snippet::complete_item_snippet(acc, ctx, path_ctx);
|
||||||
|
completions::vis::complete_vis_path(acc, ctx, path_ctx);
|
||||||
|
}
|
||||||
|
Some(NameRefKind::DotAccess(dot_access)) => {
|
||||||
|
completions::dot::complete_dot(acc, ctx, dot_access);
|
||||||
|
completions::postfix::complete_postfix(acc, ctx, dot_access);
|
||||||
|
}
|
||||||
|
Some(NameRefKind::Keyword(item)) => {
|
||||||
|
completions::keyword::complete_special_keywords(acc, ctx, item);
|
||||||
|
}
|
||||||
|
Some(NameRefKind::RecordExpr(record_expr)) => {
|
||||||
|
completions::record::complete_record_expr_fields_record_expr(
|
||||||
|
acc,
|
||||||
|
ctx,
|
||||||
|
record_expr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IdentContext::Lifetime(lifetime_ctx) => {
|
||||||
|
completions::lifetime::complete_label(acc, ctx, lifetime_ctx);
|
||||||
|
completions::lifetime::complete_lifetime(acc, ctx, lifetime_ctx);
|
||||||
|
}
|
||||||
|
IdentContext::String { original, expanded: Some(expanded) } => {
|
||||||
|
completions::extern_abi::complete_extern_abi(acc, ctx, expanded);
|
||||||
|
completions::format_string::format_string(acc, ctx, original, expanded);
|
||||||
|
}
|
||||||
|
IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: Some(attr) } => {
|
||||||
|
completions::attribute::complete_known_attribute_input(acc, ctx, attr);
|
||||||
|
}
|
||||||
|
IdentContext::UnexpandedAttrTT { .. } | IdentContext::String { .. } => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pattern_ctx) = &ctx.pattern_ctx {
|
||||||
|
completions::fn_param::complete_fn_param(acc, ctx, pattern_ctx);
|
||||||
|
completions::record::complete_record_pattern_fields(acc, ctx, pattern_ctx);
|
||||||
|
// FIXME: this check is odd, we shouldn't need this?
|
||||||
|
if !matches!(
|
||||||
|
ctx.ident_ctx,
|
||||||
|
IdentContext::NameRef(NameRefContext { kind: Some(NameRefKind::Path(_)), .. })
|
||||||
|
) {
|
||||||
|
completions::pattern::complete_pattern(acc, ctx, pattern_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This should be split
|
||||||
|
completions::flyimport::import_on_the_fly(acc, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(completions)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves additional completion data at the position given.
|
/// Resolves additional completion data at the position given.
|
||||||
|
|
|
@ -17,7 +17,7 @@ use ide_db::{
|
||||||
use syntax::{SmolStr, SyntaxKind, TextRange};
|
use syntax::{SmolStr, SyntaxKind, TextRange};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{PathCompletionCtx, PathKind},
|
context::{IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind},
|
||||||
item::{Builder, CompletionRelevanceTypeMatch},
|
item::{Builder, CompletionRelevanceTypeMatch},
|
||||||
render::{function::render_fn, literal::render_variant_lit, macro_::render_macro},
|
render::{function::render_fn, literal::render_variant_lit, macro_::render_macro},
|
||||||
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
|
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
|
||||||
|
@ -75,7 +75,13 @@ impl<'a> RenderContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn path_is_call(&self) -> bool {
|
pub(crate) fn path_is_call(&self) -> bool {
|
||||||
self.completion.path_context().map_or(false, |it| it.has_call_parens)
|
matches!(
|
||||||
|
self.completion.ident_ctx,
|
||||||
|
IdentContext::NameRef(NameRefContext {
|
||||||
|
kind: Some(NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. })),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_deprecated(&self, def: impl HasAttrs) -> bool {
|
fn is_deprecated(&self, def: impl HasAttrs) -> bool {
|
||||||
|
@ -285,8 +291,15 @@ fn render_resolution_simple_(
|
||||||
|
|
||||||
// Add `<>` for generic types
|
// Add `<>` for generic types
|
||||||
let type_path_no_ty_args = matches!(
|
let type_path_no_ty_args = matches!(
|
||||||
ctx.completion.path_context(),
|
ctx.completion.ident_ctx,
|
||||||
Some(PathCompletionCtx { kind: PathKind::Type { .. }, has_type_args: false, .. })
|
IdentContext::NameRef(NameRefContext {
|
||||||
|
kind: Some(NameRefKind::Path(PathCompletionCtx {
|
||||||
|
kind: PathKind::Type { .. },
|
||||||
|
has_type_args: false,
|
||||||
|
..
|
||||||
|
})),
|
||||||
|
..
|
||||||
|
})
|
||||||
) && ctx.completion.config.callable.is_some();
|
) && ctx.completion.config.callable.is_some();
|
||||||
if type_path_no_ty_args {
|
if type_path_no_ty_args {
|
||||||
if let Some(cap) = ctx.snippet_cap() {
|
if let Some(cap) = ctx.snippet_cap() {
|
||||||
|
@ -937,7 +950,6 @@ fn main() -> RawIdentTable {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_parens_in_use_item() {
|
fn no_parens_in_use_item() {
|
||||||
cov_mark::check!(no_parens_in_use_item);
|
|
||||||
check_edit(
|
check_edit(
|
||||||
"foo",
|
"foo",
|
||||||
r#"
|
r#"
|
||||||
|
|
|
@ -8,7 +8,7 @@ use syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{
|
context::{
|
||||||
CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind,
|
CompletionContext, DotAccess, DotAccessKind, IdentContext, NameRefContext, NameRefKind,
|
||||||
PathCompletionCtx, PathKind,
|
PathCompletionCtx, PathKind,
|
||||||
},
|
},
|
||||||
item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
|
item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
|
||||||
|
@ -91,9 +91,10 @@ fn render(
|
||||||
.lookup_by(name.to_smol_str());
|
.lookup_by(name.to_smol_str());
|
||||||
|
|
||||||
match completion.config.snippet_cap {
|
match completion.config.snippet_cap {
|
||||||
Some(cap) if should_add_parens(completion) => {
|
Some(cap) => {
|
||||||
let (self_param, params) = params(completion, func, &func_kind);
|
if let Some((self_param, params)) = params(completion, func, &func_kind) {
|
||||||
add_call_parens(&mut item, completion, cap, call, self_param, params);
|
add_call_parens(&mut item, completion, cap, call, self_param, params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -194,48 +195,6 @@ fn ref_of_param(ctx: &CompletionContext, arg: &str, ty: &hir::Type) -> &'static
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_add_parens(ctx: &CompletionContext) -> bool {
|
|
||||||
if ctx.config.callable.is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
match ctx.path_context() {
|
|
||||||
Some(PathCompletionCtx { kind: PathKind::Expr { .. }, has_call_parens: true, .. }) => {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
Some(PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. }) => {
|
|
||||||
cov_mark::hit!(no_parens_in_use_item);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
if matches!(
|
|
||||||
ctx.nameref_ctx(),
|
|
||||||
Some(NameRefContext {
|
|
||||||
kind: Some(NameRefKind::DotAccess(DotAccess {
|
|
||||||
kind: DotAccessKind::Method { has_parens: true },
|
|
||||||
..
|
|
||||||
})),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't add parentheses if the expected type is some function reference.
|
|
||||||
if let Some(ty) = &ctx.expected_type {
|
|
||||||
// FIXME: check signature matches?
|
|
||||||
if ty.is_fn() {
|
|
||||||
cov_mark::hit!(no_call_parens_if_fn_ptr_needed);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nothing prevents us from adding parentheses
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
|
fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
|
||||||
let mut ret_ty = func.ret_type(db);
|
let mut ret_ty = func.ret_type(db);
|
||||||
let mut detail = String::new();
|
let mut detail = String::new();
|
||||||
|
@ -285,13 +244,52 @@ fn params(
|
||||||
ctx: &CompletionContext<'_>,
|
ctx: &CompletionContext<'_>,
|
||||||
func: hir::Function,
|
func: hir::Function,
|
||||||
func_kind: &FuncKind,
|
func_kind: &FuncKind,
|
||||||
) -> (Option<hir::SelfParam>, Vec<hir::Param>) {
|
) -> Option<(Option<hir::SelfParam>, Vec<hir::Param>)> {
|
||||||
let self_param = if ctx.has_dot_receiver() || matches!(func_kind, FuncKind::Method(Some(_))) {
|
if ctx.config.callable.is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let has_dot_receiver = match ctx.ident_ctx {
|
||||||
|
IdentContext::NameRef(NameRefContext {
|
||||||
|
kind:
|
||||||
|
Some(NameRefKind::DotAccess(DotAccess {
|
||||||
|
kind: DotAccessKind::Method { has_parens: true },
|
||||||
|
..
|
||||||
|
})),
|
||||||
|
..
|
||||||
|
}) => return None,
|
||||||
|
IdentContext::NameRef(NameRefContext {
|
||||||
|
kind: Some(NameRefKind::DotAccess(DotAccess { .. })),
|
||||||
|
..
|
||||||
|
}) => true,
|
||||||
|
IdentContext::NameRef(NameRefContext {
|
||||||
|
kind:
|
||||||
|
Some(NameRefKind::Path(
|
||||||
|
PathCompletionCtx {
|
||||||
|
kind: PathKind::Expr { .. }, has_call_parens: true, ..
|
||||||
|
}
|
||||||
|
| PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. },
|
||||||
|
)),
|
||||||
|
..
|
||||||
|
}) => return None,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't add parentheses if the expected type is some function reference.
|
||||||
|
if let Some(ty) = &ctx.expected_type {
|
||||||
|
// FIXME: check signature matches?
|
||||||
|
if ty.is_fn() {
|
||||||
|
cov_mark::hit!(no_call_parens_if_fn_ptr_needed);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let self_param = if has_dot_receiver || matches!(func_kind, FuncKind::Method(Some(_))) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
func.self_param(ctx.db)
|
func.self_param(ctx.db)
|
||||||
};
|
};
|
||||||
(self_param, func.params_without_self(ctx.db))
|
Some((self_param, func.params_without_self(ctx.db)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -4,7 +4,9 @@ use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
|
||||||
use ide_db::SymbolKind;
|
use ide_db::SymbolKind;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{CompletionContext, PathCompletionCtx, PathKind},
|
context::{
|
||||||
|
CompletionContext, IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind,
|
||||||
|
},
|
||||||
item::{Builder, CompletionItem},
|
item::{Builder, CompletionItem},
|
||||||
render::{
|
render::{
|
||||||
compute_ref_match, compute_type_match,
|
compute_ref_match, compute_type_match,
|
||||||
|
@ -51,12 +53,19 @@ fn render(
|
||||||
) -> Option<Builder> {
|
) -> Option<Builder> {
|
||||||
let db = completion.db;
|
let db = completion.db;
|
||||||
let mut kind = thing.kind(db);
|
let mut kind = thing.kind(db);
|
||||||
let should_add_parens = match completion.path_context() {
|
let should_add_parens = match &completion.ident_ctx {
|
||||||
Some(PathCompletionCtx { has_call_parens: true, .. }) => false,
|
IdentContext::NameRef(NameRefContext {
|
||||||
Some(PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. }) => {
|
kind: Some(NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. })),
|
||||||
cov_mark::hit!(no_parens_in_use_item);
|
..
|
||||||
false
|
}) => false,
|
||||||
}
|
IdentContext::NameRef(NameRefContext {
|
||||||
|
kind:
|
||||||
|
Some(NameRefKind::Path(PathCompletionCtx {
|
||||||
|
kind: PathKind::Use | PathKind::Type { .. },
|
||||||
|
..
|
||||||
|
})),
|
||||||
|
..
|
||||||
|
}) => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use ide_db::SymbolKind;
|
||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{PathCompletionCtx, PathKind},
|
context::{IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind},
|
||||||
item::{Builder, CompletionItem},
|
item::{Builder, CompletionItem},
|
||||||
render::RenderContext,
|
render::RenderContext,
|
||||||
};
|
};
|
||||||
|
@ -33,10 +33,11 @@ fn render(
|
||||||
let is_fn_like = macro_.is_fn_like(completion.db);
|
let is_fn_like = macro_.is_fn_like(completion.db);
|
||||||
let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") };
|
let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") };
|
||||||
|
|
||||||
let needs_bang = match completion.path_context() {
|
let needs_bang = match &completion.ident_ctx {
|
||||||
Some(PathCompletionCtx { kind, has_macro_bang, .. }) => {
|
IdentContext::NameRef(NameRefContext {
|
||||||
is_fn_like && *kind != PathKind::Use && !has_macro_bang
|
kind: Some(NameRefKind::Path(PathCompletionCtx { kind, has_macro_bang, .. })),
|
||||||
}
|
..
|
||||||
|
}) => is_fn_like && *kind != PathKind::Use && !has_macro_bang,
|
||||||
_ => is_fn_like,
|
_ => is_fn_like,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,9 @@ use itertools::Itertools;
|
||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{ParamKind, PathCompletionCtx, PatternContext},
|
context::{
|
||||||
|
IdentContext, NameRefContext, NameRefKind, ParamKind, PathCompletionCtx, PatternContext,
|
||||||
|
},
|
||||||
render::{variant::visible_fields, RenderContext},
|
render::{variant::visible_fields, RenderContext},
|
||||||
CompletionItem, CompletionItemKind,
|
CompletionItem, CompletionItemKind,
|
||||||
};
|
};
|
||||||
|
@ -78,8 +80,11 @@ fn render_pat(
|
||||||
fields_omitted: bool,
|
fields_omitted: bool,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
let has_call_parens = matches!(
|
let has_call_parens = matches!(
|
||||||
ctx.completion.path_context(),
|
ctx.completion.ident_ctx,
|
||||||
Some(PathCompletionCtx { has_call_parens: true, .. })
|
IdentContext::NameRef(NameRefContext {
|
||||||
|
kind: Some(NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. })),
|
||||||
|
..
|
||||||
|
})
|
||||||
);
|
);
|
||||||
let mut pat = match kind {
|
let mut pat = match kind {
|
||||||
StructKind::Tuple if !has_call_parens => {
|
StructKind::Tuple if !has_call_parens => {
|
||||||
|
|
|
@ -160,8 +160,6 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
fd ..Default::default()
|
fd ..Default::default()
|
||||||
fd foo1 u32
|
|
||||||
fd foo2 u32
|
|
||||||
fn main() fn()
|
fn main() fn()
|
||||||
lc foo Foo
|
lc foo Foo
|
||||||
lc thing i32
|
lc thing i32
|
||||||
|
|
|
@ -179,7 +179,6 @@ impl Foo {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn enum_no_parens_in_qualified_use_tree() {
|
fn enum_no_parens_in_qualified_use_tree() {
|
||||||
cov_mark::check!(no_parens_in_use_item);
|
|
||||||
cov_mark::check!(enum_plain_qualified_use_tree);
|
cov_mark::check!(enum_plain_qualified_use_tree);
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
|
Loading…
Reference in a new issue