pub(crate) mod const_;
pub(crate) mod pattern;
pub(crate) mod type_alias;
+pub(crate) mod struct_literal;
mod builder_ext;
-use hir::{AsAssocItem, HasAttrs, HirDisplay};
+use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
use ide_db::{
helpers::{item_name, SnippetCap},
RootDatabase, SymbolKind,
};
-use syntax::TextRange;
+use syntax::{SmolStr, SyntaxKind, TextRange};
use crate::{
context::{PathCompletionContext, PathKind},
item::{CompletionRelevanceTypeMatch, ImportEdit},
render::{enum_variant::render_variant, function::render_fn, macro_::render_macro},
- CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance,
+ CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
};
/// Interface for data and methods required for items rendering.
#[derive(Debug)]
self.completion.source_range()
}
- fn is_deprecated(&self, node: impl HasAttrs) -> bool {
- let attrs = node.attrs(self.db());
+ fn is_deprecated(&self, def: impl HasAttrs) -> bool {
+ let attrs = def.attrs(self.db());
attrs.by_key("deprecated").exists() || attrs.by_key("rustc_deprecated").exists()
}
.unwrap_or(false)
}
- fn docs(&self, node: impl HasAttrs) -> Option<hir::Documentation> {
- node.docs(self.db())
+ // FIXME: remove this
+ fn docs(&self, def: impl HasAttrs) -> Option<hir::Documentation> {
+ def.docs(self.db())
}
}
ty: &hir::Type,
) -> CompletionItem {
let is_deprecated = ctx.is_deprecated(field);
- let name = field.name(ctx.db()).to_string();
+ let name = field.name(ctx.db()).to_smol_str();
let mut item = CompletionItem::new(
- CompletionKind::Reference,
+ SymbolKind::Field,
ctx.source_range(),
- receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)),
+ receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name).into()),
);
item.set_relevance(CompletionRelevance {
type_match: compute_type_match(ctx.completion, ty),
- exact_name_match: compute_exact_name_match(ctx.completion, &name),
+ exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()),
..CompletionRelevance::default()
});
- item.kind(SymbolKind::Field)
- .detail(ty.display(ctx.db()).to_string())
+ item.detail(ty.display(ctx.db()).to_string())
.set_documentation(field.docs(ctx.db()))
.set_deprecated(is_deprecated)
- .lookup_by(name);
+ .lookup_by(name.clone());
+ let is_keyword = SyntaxKind::from_keyword(name.as_str()).is_some();
+ if is_keyword && !matches!(name.as_str(), "self" | "crate" | "super" | "Self") {
+ item.insert_text(format!("r#{}", name));
+ }
if let Some(_ref_match) = compute_ref_match(ctx.completion, ty) {
// FIXME
// For now we don't properly calculate the edits for ref match
ty: &hir::Type,
) -> CompletionItem {
let mut item = CompletionItem::new(
- CompletionKind::Reference,
+ SymbolKind::Field,
ctx.source_range(),
receiver.map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)),
);
- item.kind(SymbolKind::Field)
- .detail(ty.display(ctx.db()).to_string())
- .lookup_by(field.to_string());
+ item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string());
item.build()
}
pub(crate) fn render_resolution(
ctx: RenderContext<'_>,
local_name: hir::Name,
- resolution: &hir::ScopeDef,
+ resolution: ScopeDef,
) -> Option<CompletionItem> {
render_resolution_(ctx, local_name, None, resolution)
}
ctx: RenderContext<'_>,
import_edit: ImportEdit,
) -> Option<CompletionItem> {
- let resolution = hir::ScopeDef::from(import_edit.import.original_item);
+ let resolution = ScopeDef::from(import_edit.import.original_item);
let local_name = match resolution {
- hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db),
- hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?,
- hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),
+ ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db),
+ ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?,
+ ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),
_ => item_name(ctx.db(), import_edit.import.original_item)?,
};
- render_resolution_(ctx, local_name, Some(import_edit), &resolution).map(|mut item| {
- item.completion_kind = CompletionKind::Magic;
- item
- })
+ render_resolution_(ctx, local_name, Some(import_edit), resolution)
}
fn render_resolution_(
ctx: RenderContext<'_>,
local_name: hir::Name,
import_to_add: Option<ImportEdit>,
- resolution: &hir::ScopeDef,
+ resolution: ScopeDef,
) -> Option<CompletionItem> {
let _p = profile::span("render_resolution");
use hir::ModuleDef::*;
- let completion_kind = match resolution {
- hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
- _ => CompletionKind::Reference,
- };
+ let db = ctx.db();
let kind = match resolution {
- hir::ScopeDef::ModuleDef(Function(func)) => {
- return render_fn(ctx, import_to_add, Some(local_name), *func);
- }
- hir::ScopeDef::ModuleDef(Variant(_)) if ctx.completion.is_pat_or_const.is_some() => {
- CompletionItemKind::SymbolKind(SymbolKind::Variant)
+ ScopeDef::ModuleDef(Function(func)) => {
+ return Some(render_fn(ctx, import_to_add, Some(local_name), func));
}
- hir::ScopeDef::ModuleDef(Variant(var)) => {
- let item = render_variant(ctx, import_to_add, Some(local_name), *var, None);
- return Some(item);
+ ScopeDef::ModuleDef(Variant(var)) if ctx.completion.pattern_ctx.is_none() => {
+ return Some(render_variant(ctx, import_to_add, Some(local_name), var, None));
}
- hir::ScopeDef::MacroDef(mac) => {
- let item = render_macro(ctx, import_to_add, local_name, *mac);
- return item;
+ ScopeDef::MacroDef(mac) => return render_macro(ctx, import_to_add, local_name, mac),
+ ScopeDef::Unknown => {
+ let mut item = CompletionItem::new(
+ CompletionItemKind::UnresolvedReference,
+ ctx.source_range(),
+ local_name.to_smol_str(),
+ );
+ if let Some(import_to_add) = import_to_add {
+ item.add_import(import_to_add);
+ }
+ return Some(item.build());
}
- hir::ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),
- hir::ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {
+ ScopeDef::ModuleDef(Variant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant),
+ ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),
+ ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {
hir::Adt::Struct(_) => SymbolKind::Struct,
hir::Adt::Union(_) => SymbolKind::Union,
hir::Adt::Enum(_) => SymbolKind::Enum,
}),
- hir::ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const),
- hir::ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static),
- hir::ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait),
- hir::ScopeDef::ModuleDef(TypeAlias(..)) => {
- CompletionItemKind::SymbolKind(SymbolKind::TypeAlias)
- }
- hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
- hir::ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {
+ ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const),
+ ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static),
+ ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait),
+ ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias),
+ ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
+ ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {
hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam,
hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam,
hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,
}),
- hir::ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
- hir::ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),
- hir::ScopeDef::AdtSelfType(..) | hir::ScopeDef::ImplSelfType(..) => {
+ ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
+ ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),
+ ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {
CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
}
- hir::ScopeDef::Unknown => {
- let mut item = CompletionItem::new(
- CompletionKind::Reference,
- ctx.source_range(),
- local_name.to_string(),
- );
- item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add);
- return Some(item.build());
- }
};
- let local_name = local_name.to_string();
- let mut item = CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone());
- if let hir::ScopeDef::Local(local) = resolution {
- let ty = local.ty(ctx.db());
+ let local_name = local_name.to_smol_str();
+ let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.clone());
+ if let ScopeDef::Local(local) = resolution {
+ let ty = local.ty(db);
if !ty.is_unknown() {
- item.detail(ty.display(ctx.db()).to_string());
+ item.detail(ty.display(db).to_string());
}
item.set_relevance(CompletionRelevance {
};
// Add `<>` for generic types
- if matches!(
+ let type_path_no_ty_args = matches!(
ctx.completion.path_context,
Some(PathCompletionContext { kind: Some(PathKind::Type), has_type_args: false, .. })
- ) && ctx.completion.config.add_call_parenthesis
- {
+ ) && ctx.completion.config.add_call_parenthesis;
+ if type_path_no_ty_args {
if let Some(cap) = ctx.snippet_cap() {
let has_non_default_type_params = match resolution {
- hir::ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db()),
- hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db()),
+ ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(db),
+ ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(db),
_ => false,
};
if has_non_default_type_params {
cov_mark::hit!(inserts_angle_brackets_for_generics);
item.lookup_by(local_name.clone())
- .label(format!("{}<…>", local_name))
+ .label(SmolStr::from_iter([&local_name, "<…>"]))
.insert_snippet(cap, format!("{}<$0>", local_name));
}
}
}
- item.kind(kind)
- .add_import(import_to_add)
- .set_documentation(scope_def_docs(ctx.db(), resolution))
+ item.set_documentation(scope_def_docs(db, resolution))
.set_deprecated(scope_def_is_deprecated(&ctx, resolution));
+
+ if let Some(import_to_add) = import_to_add {
+ item.add_import(import_to_add);
+ }
Some(item.build())
}
-fn scope_def_docs(db: &RootDatabase, resolution: &hir::ScopeDef) -> Option<hir::Documentation> {
+fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<hir::Documentation> {
use hir::ModuleDef::*;
match resolution {
- hir::ScopeDef::ModuleDef(Module(it)) => it.docs(db),
- hir::ScopeDef::ModuleDef(Adt(it)) => it.docs(db),
- hir::ScopeDef::ModuleDef(Variant(it)) => it.docs(db),
- hir::ScopeDef::ModuleDef(Const(it)) => it.docs(db),
- hir::ScopeDef::ModuleDef(Static(it)) => it.docs(db),
- hir::ScopeDef::ModuleDef(Trait(it)) => it.docs(db),
- hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db),
+ ScopeDef::ModuleDef(Module(it)) => it.docs(db),
+ ScopeDef::ModuleDef(Adt(it)) => it.docs(db),
+ ScopeDef::ModuleDef(Variant(it)) => it.docs(db),
+ ScopeDef::ModuleDef(Const(it)) => it.docs(db),
+ ScopeDef::ModuleDef(Static(it)) => it.docs(db),
+ ScopeDef::ModuleDef(Trait(it)) => it.docs(db),
+ ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db),
_ => None,
}
}
-fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: &hir::ScopeDef) -> bool {
+fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> bool {
match resolution {
- hir::ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(*it),
- hir::ScopeDef::MacroDef(it) => ctx.is_deprecated(*it),
- hir::ScopeDef::GenericParam(it) => ctx.is_deprecated(*it),
- hir::ScopeDef::AdtSelfType(it) => ctx.is_deprecated(*it),
+ ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(it),
+ ScopeDef::MacroDef(it) => ctx.is_deprecated(it),
+ ScopeDef::GenericParam(it) => ctx.is_deprecated(it),
+ ScopeDef::AdtSelfType(it) => ctx.is_deprecated(it),
_ => false,
}
}
#[cfg(test)]
mod tests {
+ use std::cmp;
+
use expect_test::{expect, Expect};
+ use ide_db::SymbolKind;
use itertools::Itertools;
use crate::{
item::CompletionRelevanceTypeMatch,
tests::{check_edit, do_completion, get_all_items, TEST_CONFIG},
- CompletionKind, CompletionRelevance,
+ CompletionItem, CompletionItemKind, CompletionRelevance,
};
- fn check(ra_fixture: &str, expect: Expect) {
- let actual = do_completion(ra_fixture, CompletionKind::Reference);
+ #[track_caller]
+ fn check(ra_fixture: &str, kind: impl Into<CompletionItemKind>, expect: Expect) {
+ let actual = do_completion(ra_fixture, kind.into());
expect.assert_debug_eq(&actual);
}
- fn check_relevance(ra_fixture: &str, expect: Expect) {
- fn display_relevance(relevance: CompletionRelevance) -> String {
- let relevance_factors = vec![
- (relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"),
- (
- relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify),
- "type_could_unify",
- ),
- (relevance.exact_name_match, "name"),
- (relevance.is_local, "local"),
- ]
- .into_iter()
- .filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
- .join("+");
+ #[track_caller]
+ fn check_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) {
+ let actual: Vec<_> =
+ kinds.iter().flat_map(|&kind| do_completion(ra_fixture, kind)).collect();
+ expect.assert_debug_eq(&actual);
+ }
- format!("[{}]", relevance_factors)
- }
+ #[track_caller]
+ fn check_relevance_for_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) {
+ let mut actual = get_all_items(TEST_CONFIG, ra_fixture);
+ actual.retain(|it| kinds.contains(&it.kind()));
+ actual.sort_by_key(|it| cmp::Reverse(it.relevance().score()));
+ check_relevance_(actual, expect);
+ }
+
+ #[track_caller]
+ fn check_relevance(ra_fixture: &str, expect: Expect) {
+ let mut actual = get_all_items(TEST_CONFIG, ra_fixture);
+ actual.retain(|it| it.kind() != CompletionItemKind::Snippet);
+ actual.retain(|it| it.kind() != CompletionItemKind::Keyword);
+ actual.retain(|it| it.kind() != CompletionItemKind::BuiltinType);
+ actual.sort_by_key(|it| cmp::Reverse(it.relevance().score()));
+ check_relevance_(actual, expect);
+ }
- let actual = get_all_items(TEST_CONFIG, ra_fixture)
+ #[track_caller]
+ fn check_relevance_(actual: Vec<CompletionItem>, expect: Expect) {
+ let actual = actual
.into_iter()
- .filter(|it| it.completion_kind == CompletionKind::Reference)
.flat_map(|it| {
let mut items = vec![];
- let tag = it.kind().unwrap().tag();
+ let tag = it.kind().tag();
let relevance = display_relevance(it.relevance());
items.push(format!("{} {} {}\n", tag, it.label(), relevance));
.collect::<String>();
expect.assert_eq(&actual);
+
+ fn display_relevance(relevance: CompletionRelevance) -> String {
+ let relevance_factors = vec![
+ (relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"),
+ (
+ relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify),
+ "type_could_unify",
+ ),
+ (relevance.exact_name_match, "name"),
+ (relevance.is_local, "local"),
+ (relevance.exact_postfix_snippet_match, "snippet"),
+ ]
+ .into_iter()
+ .filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
+ .join("+");
+
+ format!("[{}]", relevance_factors)
+ }
}
#[test]
fn main() { Foo::Fo$0 }
"#,
+ SymbolKind::Variant,
expect![[r#"
[
CompletionItem {
kind: SymbolKind(
Variant,
),
- detail: "{ x: i32, y: i32 }",
+ detail: "{x: i32, y: i32}",
},
]
"#]],
fn main() { Foo::Fo$0 }
"#,
+ SymbolKind::Variant,
expect![[r#"
[
CompletionItem {
fn main() { fo$0 }
"#,
+ SymbolKind::Function,
expect![[r#"
[
CompletionItem {
fn main() { Foo::Fo$0 }
"#,
+ SymbolKind::Variant,
expect![[r#"
[
CompletionItem {
#[test]
fn lookup_enums_by_two_qualifiers() {
- check(
+ check_kinds(
r#"
mod m {
pub enum Spam { Foo, Bar(i32) }
}
fn main() { let _: m::Spam = S$0 }
"#,
+ &[
+ CompletionItemKind::SymbolKind(SymbolKind::Function),
+ CompletionItemKind::SymbolKind(SymbolKind::Module),
+ CompletionItemKind::SymbolKind(SymbolKind::Variant),
+ ],
expect![[r#"
[
+ CompletionItem {
+ label: "main()",
+ source_range: 75..76,
+ delete: 75..76,
+ insert: "main()$0",
+ kind: SymbolKind(
+ Function,
+ ),
+ lookup: "main",
+ detail: "fn()",
+ },
+ CompletionItem {
+ label: "m",
+ source_range: 75..76,
+ delete: 75..76,
+ insert: "m",
+ kind: SymbolKind(
+ Module,
+ ),
+ },
CompletionItem {
label: "Spam::Bar(…)",
source_range: 75..76,
Exact,
),
is_local: false,
+ exact_postfix_snippet_match: false,
},
trigger_call_info: true,
},
- CompletionItem {
- label: "m",
- source_range: 75..76,
- delete: 75..76,
- insert: "m",
- kind: SymbolKind(
- Module,
- ),
- },
CompletionItem {
label: "m::Spam::Foo",
source_range: 75..76,
Exact,
),
is_local: false,
+ exact_postfix_snippet_match: false,
},
},
- CompletionItem {
- label: "main()",
- source_range: 75..76,
- delete: 75..76,
- insert: "main()$0",
- kind: SymbolKind(
- Function,
- ),
- lookup: "main",
- detail: "fn()",
- },
]
"#]],
)
fn main() { som$0 }
"#,
+ SymbolKind::Function,
expect![[r#"
[
CompletionItem {
struct A { #[deprecated] the_field: u32 }
fn foo() { A { the$0 } }
"#,
+ SymbolKind::Field,
expect![[r#"
[
CompletionItem {
CouldUnify,
),
is_local: false,
+ exact_postfix_snippet_match: false,
},
},
]
#[test]
fn renders_docs() {
- check(
+ check_kinds(
r#"
struct S {
/// Field docs
/// Method docs
fn bar(self) { self.$0 }
}"#,
+ &[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)],
expect![[r#"
[
CompletionItem {
"#]],
);
- check(
+ check_kinds(
r#"
use self::my$0;
}
use self::E::*;
"#,
+ &[
+ CompletionItemKind::SymbolKind(SymbolKind::Module),
+ CompletionItemKind::SymbolKind(SymbolKind::Variant),
+ CompletionItemKind::SymbolKind(SymbolKind::Enum),
+ ],
expect![[r#"
[
CompletionItem {
- label: "E",
+ label: "my",
source_range: 10..12,
delete: 10..12,
- insert: "E",
+ insert: "my",
kind: SymbolKind(
- Enum,
+ Module,
),
documentation: Documentation(
- "enum docs",
+ "mod docs",
),
},
CompletionItem {
),
},
CompletionItem {
- label: "my",
+ label: "E",
source_range: 10..12,
delete: 10..12,
- insert: "my",
+ insert: "E",
kind: SymbolKind(
- Module,
+ Enum,
),
documentation: Documentation(
- "mod docs",
+ "enum docs",
),
},
]
}
fn foo(s: S) { s.$0 }
"#,
+ CompletionItemKind::Method,
expect![[r#"
[
CompletionItem {
fn main() -> ManualVtable {
ManualVtable { f: foo }
}
+"#,
+ );
+ check_edit(
+ "type",
+ r#"
+struct RawIdentTable { r#type: u32 }
+
+fn main() -> RawIdentTable {
+ RawIdentTable { t$0: 42 }
+}
+"#,
+ r#"
+struct RawIdentTable { r#type: u32 }
+
+fn main() -> RawIdentTable {
+ RawIdentTable { r#type: 42 }
+}
"#,
);
}
fn foo(s: S) { test(s.$0) }
"#,
expect![[r#"
- fd foo []
fd bar [type+name]
fd baz [type]
+ fd foo []
"#]],
);
}
fn foo(a: A) { B { bar: a.$0 }; }
"#,
expect![[r#"
- fd foo []
fd bar [type+name]
fd baz [type]
+ fd foo []
"#]],
)
}
fn foo(a: A) { f(B { bar: a.$0 }); }
"#,
expect![[r#"
- fd foo []
fd bar [type+name]
fd baz [type]
+ fd foo []
"#]],
);
}
fn f() { A { bar: b$0 }; }
"#,
expect![[r#"
+ fn bar() [type+name]
fn baz() [type]
st A []
- fn bar() [type+name]
fn f() []
"#]],
);
#[test]
fn struct_field_method_ref() {
- check(
+ check_kinds(
r#"
struct Foo { bar: u32 }
impl Foo { fn baz(&self) -> u32 { 0 } }
fn foo(f: Foo) { let _: &u32 = f.b$0 }
"#,
+ &[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)],
// FIXME
// Ideally we'd also suggest &f.bar and &f.baz() as exact
// type matches. See #8058.
expect![[r#"
[
+ CompletionItem {
+ label: "baz()",
+ source_range: 98..99,
+ delete: 98..99,
+ insert: "baz()$0",
+ kind: Method,
+ lookup: "baz",
+ detail: "fn(&self) -> u32",
+ },
CompletionItem {
label: "bar",
source_range: 98..99,
),
detail: "u32",
},
- CompletionItem {
- label: "baz()",
- source_range: 98..99,
- delete: 98..99,
- insert: "baz()$0",
- kind: Method,
- lookup: "baz",
- detail: "fn(&self) -> u32",
- },
]
"#]],
);
}
"#,
expect![[r#"
+ lc foo [type+local]
ev Foo::A(…) [type_could_unify]
ev Foo::B [type_could_unify]
- lc foo [type+local]
+ fn foo() []
en Foo []
fn baz() []
fn bar() []
- fn foo() []
+ "#]],
+ );
+ }
+
+ #[test]
+ fn postfix_completion_relevance() {
+ check_relevance_for_kinds(
+ r#"
+mod ops {
+ pub trait Not {
+ type Output;
+ fn not(self) -> Self::Output;
+ }
+
+ impl Not for bool {
+ type Output = bool;
+ fn not(self) -> bool { if self { false } else { true }}
+ }
+}
+
+fn main() {
+ let _: bool = (9 > 2).not$0;
+}
+ "#,
+ &[CompletionItemKind::Snippet, CompletionItemKind::Method],
+ expect![[r#"
+ sn not [snippet]
+ me not() (use ops::Not) [type_could_unify]
+ sn if []
+ sn while []
+ sn ref []
+ sn refm []
+ sn match []
+ sn box []
+ sn dbg []
+ sn dbgr []
+ sn call []
"#]],
);
}