Skip to content

Commit 5a711d4

Browse files
bors[bot]Veykril
andauthored
Merge #11210
11210: feat: Deprioritize ops methods in completion r=Veykril a=Veykril Fixes #10593 Co-authored-by: Lukas Wirth <[email protected]>
2 parents 85bcca6 + 4901ea3 commit 5a711d4

File tree

8 files changed

+130
-38
lines changed

8 files changed

+130
-38
lines changed

crates/hir/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,6 +1610,12 @@ pub struct Trait {
16101610
}
16111611

16121612
impl Trait {
1613+
pub fn lang(db: &dyn HirDatabase, krate: Crate, name: &Name) -> Option<Trait> {
1614+
db.lang_item(krate.into(), name.to_smol_str())
1615+
.and_then(LangItemTarget::as_trait)
1616+
.map(Into::into)
1617+
}
1618+
16131619
pub fn module(self, db: &dyn HirDatabase) -> Module {
16141620
Module { id: self.id.lookup(db.upcast()).container }
16151621
}

crates/hir_def/src/attr.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ impl Attrs {
255255
}
256256
}
257257

258+
pub fn lang(&self) -> Option<&SmolStr> {
259+
self.by_key("lang").string_value()
260+
}
261+
258262
pub fn docs(&self) -> Option<Documentation> {
259263
let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_deref()? {
260264
AttrInput::Literal(s) => Some(s),
@@ -775,20 +779,20 @@ impl Attr {
775779
}
776780

777781
#[derive(Debug, Clone, Copy)]
778-
pub struct AttrQuery<'a> {
779-
attrs: &'a Attrs,
782+
pub struct AttrQuery<'attr> {
783+
attrs: &'attr Attrs,
780784
key: &'static str,
781785
}
782786

783-
impl<'a> AttrQuery<'a> {
784-
pub fn tt_values(self) -> impl Iterator<Item = &'a Subtree> {
787+
impl<'attr> AttrQuery<'attr> {
788+
pub fn tt_values(self) -> impl Iterator<Item = &'attr Subtree> {
785789
self.attrs().filter_map(|attr| match attr.input.as_deref()? {
786790
AttrInput::TokenTree(it, _) => Some(it),
787791
_ => None,
788792
})
789793
}
790794

791-
pub fn string_value(self) -> Option<&'a SmolStr> {
795+
pub fn string_value(self) -> Option<&'attr SmolStr> {
792796
self.attrs().find_map(|attr| match attr.input.as_deref()? {
793797
AttrInput::Literal(it) => Some(it),
794798
_ => None,
@@ -799,7 +803,7 @@ impl<'a> AttrQuery<'a> {
799803
self.attrs().next().is_some()
800804
}
801805

802-
pub fn attrs(self) -> impl Iterator<Item = &'a Attr> + Clone {
806+
pub fn attrs(self) -> impl Iterator<Item = &'attr Attr> + Clone {
803807
let key = self.key;
804808
self.attrs
805809
.iter()

crates/hir_def/src/lang_item.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ impl LangItems {
144144
let _p = profile::span("lang_item_query");
145145
let lang_items = db.crate_lang_items(start_crate);
146146
let start_crate_target = lang_items.items.get(&item);
147-
if let Some(target) = start_crate_target {
148-
return Some(*target);
147+
if let Some(&target) = start_crate_target {
148+
return Some(target);
149149
}
150150
db.crate_graph()[start_crate]
151151
.dependencies

crates/hir_expand/src/name.rs

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -309,39 +309,45 @@ pub mod known {
309309
wrapping_mul,
310310
wrapping_sub,
311311
// known methods of lang items
312-
add,
313-
mul,
314-
sub,
315-
div,
316-
rem,
317-
shl,
318-
shr,
319-
bitxor,
320-
bitor,
321-
bitand,
322-
add_assign,
323-
mul_assign,
324-
sub_assign,
325-
div_assign,
326-
rem_assign,
327-
shl_assign,
328-
shr_assign,
329-
bitxor_assign,
330-
bitor_assign,
331-
bitand_assign,
332312
eq,
333313
ne,
334314
ge,
335315
gt,
336316
le,
337317
lt,
338318
// lang items
339-
not,
340-
neg,
319+
add_assign,
320+
add,
321+
bitand_assign,
322+
bitand,
323+
bitor_assign,
324+
bitor,
325+
bitxor_assign,
326+
bitxor,
327+
deref_mut,
328+
deref,
329+
div_assign,
330+
div,
331+
fn_mut,
332+
fn_once,
341333
future_trait,
342-
owned_box,
343334
index,
344-
partial_ord
335+
index_mut,
336+
mul_assign,
337+
mul,
338+
neg,
339+
not,
340+
owned_box,
341+
partial_ord,
342+
r#fn,
343+
rem_assign,
344+
rem,
345+
shl_assign,
346+
shl,
347+
shr_assign,
348+
shr,
349+
sub_assign,
350+
sub,
345351
);
346352

347353
// self/Self cannot be used as an identifier

crates/ide_completion/src/context.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::iter;
44

55
use base_db::SourceDatabaseExt;
6-
use hir::{Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo};
6+
use hir::{HasAttrs, Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo};
77
use ide_db::{
88
active_parameter::ActiveParameter,
99
base_db::{FilePosition, SourceDatabase},
@@ -85,6 +85,7 @@ pub(crate) enum ParamKind {
8585
Function,
8686
Closure,
8787
}
88+
8889
/// `CompletionContext` is created early during completion to figure out, where
8990
/// exactly is the cursor, syntax-wise.
9091
#[derive(Debug)]
@@ -120,6 +121,7 @@ pub(crate) struct CompletionContext<'a> {
120121
pub(super) lifetime_ctx: Option<LifetimeContext>,
121122
pub(super) pattern_ctx: Option<PatternContext>,
122123
pub(super) path_context: Option<PathCompletionContext>,
124+
123125
pub(super) locals: Vec<(Name, Local)>,
124126

125127
no_completion_required: bool,
@@ -308,6 +310,14 @@ impl<'a> CompletionContext<'a> {
308310
self.token.kind() == BANG && self.token.parent().map_or(false, |it| it.kind() == MACRO_CALL)
309311
}
310312

313+
/// Whether the given trait is an operator trait or not.
314+
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
315+
match trait_.attrs(self.db).lang() {
316+
Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
317+
None => false,
318+
}
319+
}
320+
311321
/// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
312322
pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
313323
let _p = profile::span("CompletionContext::process_all_names");
@@ -388,6 +398,7 @@ impl<'a> CompletionContext<'a> {
388398
locals.push((name, local));
389399
}
390400
});
401+
391402
let mut ctx = CompletionContext {
392403
sema,
393404
scope,
@@ -889,6 +900,7 @@ fn pattern_context_for(pat: ast::Pat) -> PatternContext {
889900
});
890901
PatternContext { refutability, is_param, has_type_ascription }
891902
}
903+
892904
fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
893905
syntax.covering_element(range).ancestors().find_map(N::cast)
894906
}
@@ -915,6 +927,37 @@ fn has_ref(token: &SyntaxToken) -> bool {
915927
token.kind() == T![&]
916928
}
917929

930+
const OP_TRAIT_LANG_NAMES: &[&str] = &[
931+
"add_assign",
932+
"add",
933+
"bitand_assign",
934+
"bitand",
935+
"bitor_assign",
936+
"bitor",
937+
"bitxor_assign",
938+
"bitxor",
939+
"deref_mut",
940+
"deref",
941+
"div_assign",
942+
"div",
943+
"fn_mut",
944+
"fn_once",
945+
"fn",
946+
"index_mut",
947+
"index",
948+
"mul_assign",
949+
"mul",
950+
"neg",
951+
"not",
952+
"rem_assign",
953+
"rem",
954+
"shl_assign",
955+
"shl",
956+
"shr_assign",
957+
"shr",
958+
"sub",
959+
"sub",
960+
];
918961
#[cfg(test)]
919962
mod tests {
920963
use expect_test::{expect, Expect};

crates/ide_completion/src/item.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ pub struct CompletionRelevance {
139139
/// }
140140
/// ```
141141
pub is_local: bool,
142+
/// Set for method completions of the `core::ops` family.
143+
pub is_op_method: bool,
142144
/// This is set in cases like these:
143145
///
144146
/// ```
@@ -175,6 +177,7 @@ pub enum CompletionRelevanceTypeMatch {
175177
}
176178

177179
impl CompletionRelevance {
180+
const BASE_LINE: u32 = 1;
178181
/// Provides a relevance score. Higher values are more relevant.
179182
///
180183
/// The absolute value of the relevance score is not meaningful, for
@@ -185,7 +188,7 @@ impl CompletionRelevance {
185188
/// See is_relevant if you need to make some judgement about score
186189
/// in an absolute sense.
187190
pub fn score(&self) -> u32 {
188-
let mut score = 0;
191+
let mut score = Self::BASE_LINE;
189192

190193
if self.exact_name_match {
191194
score += 1;
@@ -198,6 +201,9 @@ impl CompletionRelevance {
198201
if self.is_local {
199202
score += 1;
200203
}
204+
if self.is_op_method {
205+
score -= 1;
206+
}
201207
if self.exact_postfix_snippet_match {
202208
score += 100;
203209
}
@@ -208,7 +214,7 @@ impl CompletionRelevance {
208214
/// some threshold such that we think it is especially likely
209215
/// to be relevant.
210216
pub fn is_relevant(&self) -> bool {
211-
self.score() > 0
217+
self.score() > (Self::BASE_LINE + 1)
212218
}
213219
}
214220

@@ -558,6 +564,7 @@ mod tests {
558564
// This test asserts that the relevance score for these items is ascending, and
559565
// that any items in the same vec have the same score.
560566
let expected_relevance_order = vec![
567+
vec![CompletionRelevance { is_op_method: true, ..CompletionRelevance::default() }],
561568
vec![CompletionRelevance::default()],
562569
vec![
563570
CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() },
@@ -588,10 +595,8 @@ mod tests {
588595
..CompletionRelevance::default()
589596
}],
590597
vec![CompletionRelevance {
591-
exact_name_match: false,
592-
type_match: None,
593-
is_local: false,
594598
exact_postfix_snippet_match: true,
599+
..CompletionRelevance::default()
595600
}],
596601
];
597602

crates/ide_completion/src/render.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ mod tests {
400400
(relevance.exact_name_match, "name"),
401401
(relevance.is_local, "local"),
402402
(relevance.exact_postfix_snippet_match, "snippet"),
403+
(relevance.is_op_method, "op_method"),
403404
]
404405
.into_iter()
405406
.filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
@@ -580,6 +581,7 @@ fn main() { let _: m::Spam = S$0 }
580581
Exact,
581582
),
582583
is_local: false,
584+
is_op_method: false,
583585
exact_postfix_snippet_match: false,
584586
},
585587
trigger_call_info: true,
@@ -600,6 +602,7 @@ fn main() { let _: m::Spam = S$0 }
600602
Exact,
601603
),
602604
is_local: false,
605+
is_op_method: false,
603606
exact_postfix_snippet_match: false,
604607
},
605608
},
@@ -685,6 +688,7 @@ fn foo() { A { the$0 } }
685688
CouldUnify,
686689
),
687690
is_local: false,
691+
is_op_method: false,
688692
exact_postfix_snippet_match: false,
689693
},
690694
},
@@ -1349,6 +1353,23 @@ fn main() {
13491353
)
13501354
}
13511355

1356+
#[test]
1357+
fn op_method_relevances() {
1358+
check_relevance(
1359+
r#"
1360+
#[lang = "sub"]
1361+
trait Sub {
1362+
fn sub(self, other: Self) -> Self { self }
1363+
}
1364+
impl Sub for u32 {}
1365+
fn foo(a: u32) { a.$0 }
1366+
"#,
1367+
expect![[r#"
1368+
me sub(…) (as Sub) [op_method]
1369+
"#]],
1370+
)
1371+
}
1372+
13521373
#[test]
13531374
fn struct_field_method_ref() {
13541375
check_kinds(

crates/ide_completion/src/render/function.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ fn render(
7070
item.set_relevance(CompletionRelevance {
7171
type_match: compute_type_match(completion, &ret_type),
7272
exact_name_match: compute_exact_name_match(completion, &call),
73+
is_op_method: match func_type {
74+
FuncType::Method(_) => func
75+
.as_assoc_item(ctx.db())
76+
.and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db()))
77+
.map_or(false, |trait_| completion.is_ops_trait(trait_)),
78+
_ => false,
79+
},
7380
..CompletionRelevance::default()
7481
});
7582

0 commit comments

Comments
 (0)