Lookup lang attribute on assoc item trait directly

This commit is contained in:
Lukas Wirth 2022-01-11 10:07:16 +01:00
parent 94b369faa3
commit 4901ea3eef
4 changed files with 67 additions and 54 deletions

View File

@ -255,6 +255,10 @@ impl Attrs {
} }
} }
pub fn lang(&self) -> Option<&SmolStr> {
self.by_key("lang").string_value()
}
pub fn docs(&self) -> Option<Documentation> { pub fn docs(&self) -> Option<Documentation> {
let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_deref()? { let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_deref()? {
AttrInput::Literal(s) => Some(s), AttrInput::Literal(s) => Some(s),
@ -754,20 +758,20 @@ impl Attr {
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct AttrQuery<'a> { pub struct AttrQuery<'attr> {
attrs: &'a Attrs, attrs: &'attr Attrs,
key: &'static str, key: &'static str,
} }
impl<'a> AttrQuery<'a> { impl<'attr> AttrQuery<'attr> {
pub fn tt_values(self) -> impl Iterator<Item = &'a Subtree> { pub fn tt_values(self) -> impl Iterator<Item = &'attr Subtree> {
self.attrs().filter_map(|attr| match attr.input.as_deref()? { self.attrs().filter_map(|attr| match attr.input.as_deref()? {
AttrInput::TokenTree(it, _) => Some(it), AttrInput::TokenTree(it, _) => Some(it),
_ => None, _ => None,
}) })
} }
pub fn string_value(self) -> Option<&'a SmolStr> { pub fn string_value(self) -> Option<&'attr SmolStr> {
self.attrs().find_map(|attr| match attr.input.as_deref()? { self.attrs().find_map(|attr| match attr.input.as_deref()? {
AttrInput::Literal(it) => Some(it), AttrInput::Literal(it) => Some(it),
_ => None, _ => None,
@ -778,7 +782,7 @@ impl<'a> AttrQuery<'a> {
self.attrs().next().is_some() self.attrs().next().is_some()
} }
pub fn attrs(self) -> impl Iterator<Item = &'a Attr> + Clone { pub fn attrs(self) -> impl Iterator<Item = &'attr Attr> + Clone {
let key = self.key; let key = self.key;
self.attrs self.attrs
.iter() .iter()

View File

@ -3,14 +3,13 @@
use std::iter; use std::iter;
use base_db::SourceDatabaseExt; use base_db::SourceDatabaseExt;
use hir::{known, Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo}; use hir::{HasAttrs, Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo};
use ide_db::{ use ide_db::{
active_parameter::ActiveParameter, active_parameter::ActiveParameter,
base_db::{FilePosition, SourceDatabase}, base_db::{FilePosition, SourceDatabase},
helpers::FamousDefs, helpers::FamousDefs,
RootDatabase, RootDatabase,
}; };
use rustc_hash::FxHashSet;
use syntax::{ use syntax::{
algo::find_node_at_offset, algo::find_node_at_offset,
ast::{self, HasName, NameOrNameRef}, ast::{self, HasName, NameOrNameRef},
@ -124,8 +123,6 @@ pub(crate) struct CompletionContext<'a> {
pub(super) path_context: Option<PathCompletionContext>, pub(super) path_context: Option<PathCompletionContext>,
pub(super) locals: Vec<(Name, Local)>, pub(super) locals: Vec<(Name, Local)>,
/// Operator traits defined in the project
pub(super) ops_traits: FxHashSet<hir::Trait>,
no_completion_required: bool, no_completion_required: bool,
} }
@ -315,7 +312,10 @@ impl<'a> CompletionContext<'a> {
/// Whether the given trait is an operator trait or not. /// Whether the given trait is an operator trait or not.
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
self.ops_traits.contains(&trait_) match trait_.attrs(self.db).lang() {
Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
None => false,
}
} }
/// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items. /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
@ -398,16 +398,6 @@ impl<'a> CompletionContext<'a> {
locals.push((name, local)); locals.push((name, local));
} }
}); });
let mut ops_traits =
FxHashSet::with_capacity_and_hasher(OP_TRAIT_LANG_NAMES.len(), Default::default());
if let Some(krate) = krate {
let _p = profile::span("CompletionContext::new ops");
for trait_ in
OP_TRAIT_LANG_NAMES.iter().filter_map(|item| hir::Trait::lang(db, krate, item))
{
ops_traits.insert(trait_);
}
}
let mut ctx = CompletionContext { let mut ctx = CompletionContext {
sema, sema,
@ -434,7 +424,6 @@ impl<'a> CompletionContext<'a> {
locals, locals,
incomplete_let: false, incomplete_let: false,
no_completion_required: false, no_completion_required: false,
ops_traits,
}; };
ctx.expand_and_fill( ctx.expand_and_fill(
original_file.syntax().clone(), original_file.syntax().clone(),
@ -938,36 +927,36 @@ fn has_ref(token: &SyntaxToken) -> bool {
token.kind() == T![&] token.kind() == T![&]
} }
const OP_TRAIT_LANG_NAMES: &[hir::Name] = &[ const OP_TRAIT_LANG_NAMES: &[&str] = &[
known::add_assign, "add_assign",
known::add, "add",
known::bitand_assign, "bitand_assign",
known::bitand, "bitand",
known::bitor_assign, "bitor_assign",
known::bitor, "bitor",
known::bitxor_assign, "bitxor_assign",
known::bitxor, "bitxor",
known::deref_mut, "deref_mut",
known::deref, "deref",
known::div_assign, "div_assign",
known::div, "div",
known::fn_mut, "fn_mut",
known::fn_once, "fn_once",
known::r#fn, "fn",
known::index_mut, "index_mut",
known::index, "index",
known::mul_assign, "mul_assign",
known::mul, "mul",
known::neg, "neg",
known::not, "not",
known::rem_assign, "rem_assign",
known::rem, "rem",
known::shl_assign, "shl_assign",
known::shl, "shl",
known::shr_assign, "shr_assign",
known::shr, "shr",
known::sub, "sub",
known::sub, "sub",
]; ];
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -177,6 +177,7 @@ pub enum CompletionRelevanceTypeMatch {
} }
impl CompletionRelevance { impl CompletionRelevance {
const BASE_LINE: u32 = 1;
/// Provides a relevance score. Higher values are more relevant. /// Provides a relevance score. Higher values are more relevant.
/// ///
/// The absolute value of the relevance score is not meaningful, for /// The absolute value of the relevance score is not meaningful, for
@ -187,7 +188,7 @@ impl CompletionRelevance {
/// See is_relevant if you need to make some judgement about score /// See is_relevant if you need to make some judgement about score
/// in an absolute sense. /// in an absolute sense.
pub fn score(&self) -> u32 { pub fn score(&self) -> u32 {
let mut score = 0; let mut score = Self::BASE_LINE;
if self.exact_name_match { if self.exact_name_match {
score += 1; score += 1;
@ -213,7 +214,7 @@ impl CompletionRelevance {
/// some threshold such that we think it is especially likely /// some threshold such that we think it is especially likely
/// to be relevant. /// to be relevant.
pub fn is_relevant(&self) -> bool { pub fn is_relevant(&self) -> bool {
self.score() > 0 self.score() > (Self::BASE_LINE + 1)
} }
} }
@ -563,6 +564,7 @@ mod tests {
// This test asserts that the relevance score for these items is ascending, and // This test asserts that the relevance score for these items is ascending, and
// that any items in the same vec have the same score. // that any items in the same vec have the same score.
let expected_relevance_order = vec![ let expected_relevance_order = vec![
vec![CompletionRelevance { is_op_method: true, ..CompletionRelevance::default() }],
vec![CompletionRelevance::default()], vec![CompletionRelevance::default()],
vec![ vec![
CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() }, CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() },

View File

@ -400,6 +400,7 @@ mod tests {
(relevance.exact_name_match, "name"), (relevance.exact_name_match, "name"),
(relevance.is_local, "local"), (relevance.is_local, "local"),
(relevance.exact_postfix_snippet_match, "snippet"), (relevance.exact_postfix_snippet_match, "snippet"),
(relevance.is_op_method, "op_method"),
] ]
.into_iter() .into_iter()
.filter_map(|(cond, desc)| if cond { Some(desc) } else { None }) .filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
@ -1352,6 +1353,23 @@ fn main() {
) )
} }
#[test]
fn op_method_relevances() {
check_relevance(
r#"
#[lang = "sub"]
trait Sub {
fn sub(self, other: Self) -> Self { self }
}
impl Sub for u32 {}
fn foo(a: u32) { a.$0 }
"#,
expect![[r#"
me sub() (as Sub) [op_method]
"#]],
)
}
#[test] #[test]
fn struct_field_method_ref() { fn struct_field_method_ref() {
check_kinds( check_kinds(