feat: filter out duplicate macro completions

closes #9303
This commit is contained in:
Aleksey Kladov 2021-08-03 17:36:06 +03:00
parent 314e2e75c0
commit 2f9273633b
13 changed files with 133 additions and 95 deletions

1
Cargo.lock generated
View file

@ -460,6 +460,7 @@ dependencies = [
"hir_def", "hir_def",
"hir_expand", "hir_expand",
"hir_ty", "hir_ty",
"indexmap",
"itertools", "itertools",
"log", "log",
"once_cell", "once_cell",

View file

@ -16,6 +16,7 @@ arrayvec = "0.7"
itertools = "0.10.0" itertools = "0.10.0"
smallvec = "1.4.0" smallvec = "1.4.0"
once_cell = "1" once_cell = "1"
indexmap = "1.7"
stdx = { path = "../stdx", version = "0.0.0" } stdx = { path = "../stdx", version = "0.0.0" }
syntax = { path = "../syntax", version = "0.0.0" } syntax = { path = "../syntax", version = "0.0.0" }

View file

@ -923,31 +923,28 @@ impl<'a> SemanticsScope<'a> {
} }
pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
let resolver = &self.resolver; let scope = self.resolver.names_in_scope(self.db.upcast());
for (name, entries) in scope {
resolver.process_all_names(self.db.upcast(), &mut |name, def| { for entry in entries {
let def = match def { let def = match entry {
resolver::ScopeDef::PerNs(it) => { resolver::ScopeDef::ModuleDef(it) => ScopeDef::ModuleDef(it.into()),
let items = ScopeDef::all_items(it); resolver::ScopeDef::MacroDef(it) => ScopeDef::MacroDef(it.into()),
for item in items { resolver::ScopeDef::Unknown => ScopeDef::Unknown,
f(name.clone(), item); resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()),
resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(id.into()),
resolver::ScopeDef::Local(pat_id) => {
let parent = self.resolver.body_owner().unwrap();
ScopeDef::Local(Local { parent, pat_id })
} }
return; resolver::ScopeDef::Label(label_id) => {
} let parent = self.resolver.body_owner().unwrap();
resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), ScopeDef::Label(Label { parent, label_id })
resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), }
resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(id.into()), };
resolver::ScopeDef::Local(pat_id) => { f(name.clone(), def)
let parent = resolver.body_owner().unwrap(); }
ScopeDef::Local(Local { parent, pat_id }) }
}
resolver::ScopeDef::Label(label_id) => {
let parent = resolver.body_owner().unwrap();
ScopeDef::Label(Label { parent, label_id })
}
};
f(name, def)
})
} }
/// Resolve a path as-if it was written at the given scope. This is /// Resolve a path as-if it was written at the given scope. This is

View file

@ -6,7 +6,9 @@ use hir_expand::{
name::{name, Name}, name::{name, Name},
MacroDefId, MacroDefId,
}; };
use indexmap::IndexMap;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use smallvec::SmallVec;
use crate::{ use crate::{
body::scope::{ExprScopes, ScopeId}, body::scope::{ExprScopes, ScopeId},
@ -348,10 +350,50 @@ impl Resolver {
item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros() item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros()
} }
pub fn process_all_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { /// Returns a set of names available in the current scope.
///
/// Note that this is a somewhat fuzzy concept -- internally, the compiler
/// doesn't necessary follow a strict scoping discipline. Rathe, it just
/// tells for each ident what it resolves to.
///
/// A good example is something like `str::from_utf8`. From scopes point of
/// view, this code is erroneous -- both `str` module and `str` type occupy
/// the same type namespace.
///
/// We don't try to model that super-correctly -- this functionality is
/// primarily exposed for completions.
///
/// Note that in Rust one name can be bound to several items:
///
/// ```
/// macro_rules! t { () => (()) }
/// type t = t!();
/// const t: t = t!()
/// ```
///
/// That's why we return a multimap.
///
/// The shadowing is accounted for: in
///
/// ```
/// let x = 92;
/// {
/// let x = 92;
/// $0
/// }
/// ```
///
/// there will be only one entry for `x` in the result.
///
/// The result is ordered *roughly* from the innermost scope to the
/// outermost: when the name is introduced in two namespaces in two scopes,
/// we use the position of the first scope.
pub fn names_in_scope(&self, db: &dyn DefDatabase) -> IndexMap<Name, SmallVec<[ScopeDef; 1]>> {
let mut res = ScopeNames::default();
for scope in self.scopes() { for scope in self.scopes() {
scope.process_names(db, f); scope.process_names(db, &mut res);
} }
res.map
} }
pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> { pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> {
@ -434,8 +476,11 @@ impl Resolver {
} }
} }
#[derive(Debug, PartialEq, Eq)]
pub enum ScopeDef { pub enum ScopeDef {
PerNs(PerNs), ModuleDef(ModuleDefId),
MacroDef(MacroDefId),
Unknown,
ImplSelfType(ImplId), ImplSelfType(ImplId),
AdtSelfType(AdtId), AdtSelfType(AdtId),
GenericParam(GenericParamId), GenericParam(GenericParamId),
@ -444,8 +489,7 @@ pub enum ScopeDef {
} }
impl Scope { impl Scope {
fn process_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { fn process_names(&self, db: &dyn DefDatabase, acc: &mut ScopeNames) {
let mut seen = FxHashSet::default();
match self { match self {
Scope::ModuleScope(m) => { Scope::ModuleScope(m) => {
// FIXME: should we provide `self` here? // FIXME: should we provide `self` here?
@ -456,58 +500,53 @@ impl Scope {
// }), // }),
// ); // );
m.def_map[m.module_id].scope.entries().for_each(|(name, def)| { m.def_map[m.module_id].scope.entries().for_each(|(name, def)| {
f(name.clone(), ScopeDef::PerNs(def)); acc.add_per_ns(name, def);
}); });
m.def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { m.def_map[m.module_id].scope.legacy_macros().for_each(|(name, mac)| {
let scope = PerNs::macros(macro_, Visibility::Public); acc.add(name, ScopeDef::MacroDef(mac));
seen.insert((name.clone(), scope));
f(name.clone(), ScopeDef::PerNs(scope));
}); });
m.def_map.extern_prelude().for_each(|(name, &def)| { m.def_map.extern_prelude().for_each(|(name, &def)| {
f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); acc.add(name, ScopeDef::ModuleDef(def));
}); });
BUILTIN_SCOPE.iter().for_each(|(name, &def)| { BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
f(name.clone(), ScopeDef::PerNs(def)); acc.add_per_ns(name, def);
}); });
if let Some(prelude) = m.def_map.prelude() { if let Some(prelude) = m.def_map.prelude() {
let prelude_def_map = prelude.def_map(db); let prelude_def_map = prelude.def_map(db);
prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { for (name, def) in prelude_def_map[prelude.local_id].scope.entries() {
let seen_tuple = (name.clone(), def); acc.add_per_ns(name, def)
if !seen.contains(&seen_tuple) { }
f(seen_tuple.0, ScopeDef::PerNs(def));
}
});
} }
} }
Scope::GenericParams { params, def: parent } => { Scope::GenericParams { params, def: parent } => {
let parent = *parent; let parent = *parent;
for (local_id, param) in params.types.iter() { for (local_id, param) in params.types.iter() {
if let Some(ref name) = param.name { if let Some(name) = &param.name {
let id = TypeParamId { parent, local_id }; let id = TypeParamId { parent, local_id };
f(name.clone(), ScopeDef::GenericParam(id.into())) acc.add(name, ScopeDef::GenericParam(id.into()))
} }
} }
for (local_id, param) in params.consts.iter() { for (local_id, param) in params.consts.iter() {
let id = ConstParamId { parent, local_id }; let id = ConstParamId { parent, local_id };
f(param.name.clone(), ScopeDef::GenericParam(id.into())) acc.add(&param.name, ScopeDef::GenericParam(id.into()))
} }
for (local_id, param) in params.lifetimes.iter() { for (local_id, param) in params.lifetimes.iter() {
let id = LifetimeParamId { parent, local_id }; let id = LifetimeParamId { parent, local_id };
f(param.name.clone(), ScopeDef::GenericParam(id.into())) acc.add(&param.name, ScopeDef::GenericParam(id.into()))
} }
} }
Scope::ImplDefScope(i) => { Scope::ImplDefScope(i) => {
f(name![Self], ScopeDef::ImplSelfType(*i)); acc.add(&name![Self], ScopeDef::ImplSelfType(*i));
} }
Scope::AdtScope(i) => { Scope::AdtScope(i) => {
f(name![Self], ScopeDef::AdtSelfType(*i)); acc.add(&name![Self], ScopeDef::AdtSelfType(*i));
} }
Scope::ExprScope(scope) => { Scope::ExprScope(scope) => {
if let Some((label, name)) = scope.expr_scopes.label(scope.scope_id) { if let Some((label, name)) = scope.expr_scopes.label(scope.scope_id) {
f(name, ScopeDef::Label(label)) acc.add(&name, ScopeDef::Label(label))
} }
scope.expr_scopes.entries(scope.scope_id).iter().for_each(|e| { scope.expr_scopes.entries(scope.scope_id).iter().for_each(|e| {
f(e.name().clone(), ScopeDef::Local(e.pat())); acc.add_local(e.name(), e.pat());
}); });
} }
} }
@ -651,6 +690,47 @@ fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
Some(res) Some(res)
} }
#[derive(Default)]
struct ScopeNames {
map: IndexMap<Name, SmallVec<[ScopeDef; 1]>>,
}
impl ScopeNames {
fn add(&mut self, name: &Name, def: ScopeDef) {
let set = self.map.entry(name.clone()).or_default();
if !set.contains(&def) {
set.push(def)
}
}
fn add_per_ns(&mut self, name: &Name, def: PerNs) {
if let Some(ty) = &def.types {
self.add(name, ScopeDef::ModuleDef(ty.0))
}
if let Some(val) = &def.values {
self.add(name, ScopeDef::ModuleDef(val.0))
}
if let Some(mac) = &def.macros {
self.add(name, ScopeDef::MacroDef(mac.0))
}
if def.is_none() {
self.add(name, ScopeDef::Unknown)
}
}
fn add_local(&mut self, name: &Name, pat: PatId) {
let set = self.map.entry(name.clone()).or_default();
// XXX: hack, account for local (and only local) shadowing.
//
// This should be somewhat more principled and take namespaces into
// accounts, but, alas, scoping rules are a hoax. `str` type and `str`
// module can be both available in the same scope.
if set.iter().any(|it| matches!(it, &ScopeDef::Local(_))) {
cov_mark::hit!(shadowing_shows_single_completion);
return;
}
set.push(ScopeDef::Local(pat))
}
}
pub trait HasResolver: Copy { pub trait HasResolver: Copy {
/// Builds a resolver for type references inside this def. /// Builds a resolver for type references inside this def.
fn resolver(self, db: &dyn DefDatabase) -> Resolver; fn resolver(self, db: &dyn DefDatabase) -> Resolver;

View file

@ -304,25 +304,4 @@ pub mod prelude {
"#]], "#]],
); );
} }
#[test]
fn local_variable_shadowing() {
// FIXME: this isn't actually correct, should emit `x` only once.
check(
r#"
fn main() {
let x = 92;
{
let x = 92;
x$0;
}
}
"#,
expect![[r#"
lc x i32
lc x i32
fn main() fn()
"#]],
);
}
} }

View file

@ -1348,10 +1348,10 @@ fn foo() {
lc foo [type+local] lc foo [type+local]
ev Foo::A() [type_could_unify] ev Foo::A() [type_could_unify]
ev Foo::B [type_could_unify] ev Foo::B [type_could_unify]
fn foo() []
en Foo [] en Foo []
fn baz() [] fn baz() []
fn bar() [] fn bar() []
fn foo() []
"#]], "#]],
); );
} }

View file

@ -118,7 +118,6 @@ impl Unit {
un Union un Union
ev TupleV() (u32) ev TupleV() (u32)
ct CONST ct CONST
ma makro!() #[macro_export] macro_rules! makro
me self.foo() fn(self) me self.foo() fn(self)
"##]], "##]],
); );
@ -155,6 +154,8 @@ impl Unit {
#[test] #[test]
fn shadowing_shows_single_completion() { fn shadowing_shows_single_completion() {
cov_mark::check!(shadowing_shows_single_completion);
check_empty( check_empty(
r#" r#"
fn foo() { fn foo() {
@ -165,7 +166,6 @@ fn foo() {
} }
} }
"#, "#,
// FIXME: should be only one bar here
expect![[r#" expect![[r#"
kw unsafe kw unsafe
kw match kw match
@ -182,7 +182,6 @@ fn foo() {
kw super kw super
kw crate kw crate
lc bar i32 lc bar i32
lc bar i32
fn foo() fn() fn foo() fn()
bt u32 bt u32
"#]], "#]],

View file

@ -29,7 +29,6 @@ impl Tra$0
st Unit st Unit
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
un Union un Union
ma makro!() #[macro_export] macro_rules! makro
bt u32 bt u32
"##]], "##]],
) )
@ -53,7 +52,6 @@ impl Trait for Str$0
st Unit st Unit
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
un Union un Union
ma makro!() #[macro_export] macro_rules! makro
bt u32 bt u32
"##]], "##]],
) )

View file

@ -67,7 +67,6 @@ fn in_source_file_item_list() {
kw crate kw crate
md module md module
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
ma makro!() #[macro_export] macro_rules! makro
"##]], "##]],
) )
} }
@ -172,7 +171,6 @@ fn in_impl_assoc_item_list() {
kw crate kw crate
md module md module
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
ma makro!() #[macro_export] macro_rules! makro
"##]], "##]],
) )
} }
@ -206,7 +204,6 @@ fn in_trait_assoc_item_list() {
kw crate kw crate
md module md module
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
ma makro!() #[macro_export] macro_rules! makro
"##]], "##]],
); );
} }
@ -243,7 +240,6 @@ impl Test for () {
kw crate kw crate
md module md module
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
ma makro!() #[macro_export] macro_rules! makro
"##]], "##]],
); );
} }

View file

@ -122,7 +122,6 @@ fn foo() {
bn TupleV TupleV($1)$0 bn TupleV TupleV($1)$0
ev TupleV ev TupleV
ct CONST ct CONST
ma makro!() #[macro_export] macro_rules! makro
"##]], "##]],
); );
} }
@ -143,7 +142,6 @@ fn foo() {
st Tuple st Tuple
st Unit st Unit
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
ma makro!() #[macro_export] macro_rules! makro
"##]], "##]],
); );
} }
@ -163,7 +161,6 @@ fn foo(a$0) {
st Tuple st Tuple
st Unit st Unit
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
ma makro!() #[macro_export] macro_rules! makro
"##]], "##]],
); );
} }

View file

@ -28,7 +28,6 @@ struct Foo<'lt, T, const C: usize> where $0 {}
st Unit st Unit
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
un Union un Union
ma makro!() #[macro_export] macro_rules! makro
bt u32 bt u32
"##]], "##]],
); );
@ -47,7 +46,6 @@ struct Foo<'lt, T, const C: usize> where T: $0 {}
tt Trait tt Trait
md module md module
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
ma makro!() #[macro_export] macro_rules! makro
"##]], "##]],
); );
} }
@ -67,7 +65,6 @@ struct Foo<'lt, T, const C: usize> where 'lt: $0 {}
tt Trait tt Trait
md module md module
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
ma makro!() #[macro_export] macro_rules! makro
"##]], "##]],
); );
} }
@ -85,7 +82,6 @@ struct Foo<'lt, T, const C: usize> where for<'a> T: $0 {}
tt Trait tt Trait
md module md module
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
ma makro!() #[macro_export] macro_rules! makro
"##]], "##]],
); );
} }
@ -109,7 +105,6 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
st Unit st Unit
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
un Union un Union
ma makro!() #[macro_export] macro_rules! makro
bt u32 bt u32
"##]], "##]],
); );
@ -136,7 +131,6 @@ impl Record {
st Unit st Unit
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
un Union un Union
ma makro!() #[macro_export] macro_rules! makro
bt u32 bt u32
"##]], "##]],
); );

View file

@ -31,7 +31,6 @@ struct Foo<'lt, T, const C: usize> {
st Unit st Unit
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
un Union un Union
ma makro!() #[macro_export] macro_rules! makro
bt u32 bt u32
"##]], "##]],
) )
@ -60,7 +59,6 @@ struct Foo<'lt, T, const C: usize>(f$0);
st Unit st Unit
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
un Union un Union
ma makro!() #[macro_export] macro_rules! makro
bt u32 bt u32
"##]], "##]],
) )
@ -85,7 +83,6 @@ fn x<'lt, T, const C: usize>() -> $0
st Unit st Unit
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
un Union un Union
ma makro!() #[macro_export] macro_rules! makro
bt u32 bt u32
"##]], "##]],
); );
@ -113,7 +110,6 @@ fn foo<'lt, T, const C: usize>() {
st Unit st Unit
ma makro!() #[macro_export] macro_rules! makro ma makro!() #[macro_export] macro_rules! makro
un Union un Union
ma makro!() #[macro_export] macro_rules! makro
bt u32 bt u32
"##]], "##]],
); );
@ -164,7 +160,6 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
tt Trait2 tt Trait2
un Union un Union
ct CONST ct CONST
ma makro!() #[macro_export] macro_rules! makro
bt u32 bt u32
"##]], "##]],
); );

View file

@ -839,6 +839,7 @@ crate -> krate
enum -> enum_ enum -> enum_
fn -> func fn -> func
impl -> imp impl -> imp
macro -> mac
mod -> module mod -> module
struct -> strukt struct -> strukt
trait -> trait_ trait -> trait_