Skip to content

Commit 5a7e11f

Browse files
bors[bot]Veykril
andauthored
Merge #11397
11397: internal: Refactor completion module split r=Veykril a=Veykril Currently our completion infra is split into several modules, each trying to do completions for something specific. This "something" is rather unstructured as it stands now, we have a module for `flyimporting path` completions, `unqualified` and `qualified path` completions, modules for `pattern position` completions that only try to complete extra things for patterns that aren't done in the path modules, `attribute` completions that again only try to add builtin attribute completions without adding the normal path completions and a bunch of other special "entity" completions like lifetimes, method call/field access, function param cloning, ... which serve a more specific purpose than the previous listed ones. As is evident, the former mentioned ones have some decent overlap which requires extra filtering in them so that they don't collide with each other duplicating a bunch of completions(which we had happen in the past at times). Now this overlap mostly happens with path completions(and keyword completions as well in some sense) which gives me the feeling that having `qualified` and `unqualified` path completions be separate from the rest gives us more troubles than benefits in the long run. So this is an attempt at changing this structure to instead still go by rough entity for special cases, but when it comes to paths we instead do the module split on the "path kinds"/"locations"(think pattern, type, expr position etc) that exist. This goes hand in hand with the test refactoring I have done that moved tests to "location oriented" modules as well as the `CompletionContext` refactoring that actually already started splitting the context up for path kinds. This PR moves some path completions out of the `qualified` and `unqualified` path modules namely attribute, visibility, use and pattern paths. Co-authored-by: Lukas Wirth <[email protected]>
2 parents 46b5089 + 7619c2a commit 5a7e11f

25 files changed

+700
-356
lines changed

crates/hir/src/display.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ impl HirDisplay for Module {
481481
// FIXME: Module doesn't have visibility saved in data.
482482
match self.name(f.db) {
483483
Some(name) => write!(f, "mod {}", name),
484-
None if self.crate_root(f.db) == *self => match self.krate().display_name(f.db) {
484+
None if self.is_crate_root(f.db) => match self.krate().display_name(f.db) {
485485
Some(name) => write!(f, "extern crate {}", name),
486486
None => write!(f, "extern crate {{unknown}}"),
487487
},

crates/hir/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,11 @@ impl Module {
452452
Module { id: def_map.module_id(def_map.root()) }
453453
}
454454

455+
pub fn is_crate_root(self, db: &dyn HirDatabase) -> bool {
456+
let def_map = db.crate_def_map(self.id.krate());
457+
def_map.root() == self.id.local_id
458+
}
459+
455460
/// Iterates over all child modules.
456461
pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> {
457462
let def_map = self.id.def_map(db.upcast());

crates/hir/src/semantics.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -949,12 +949,15 @@ impl<'db> SemanticsImpl<'db> {
949949
})?;
950950

951951
match res {
952-
Either::Left(path) => resolve_hir_path(
953-
self.db,
954-
&self.scope(derive.syntax()).resolver,
955-
&Path::from_known_path(path, []),
956-
)
957-
.filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))),
952+
Either::Left(path) => {
953+
let len = path.len();
954+
resolve_hir_path(
955+
self.db,
956+
&self.scope(derive.syntax()).resolver,
957+
&Path::from_known_path(path, vec![None; len]),
958+
)
959+
.filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_))))
960+
}
958961
Either::Right(derive) => derive
959962
.map(|call| MacroDef { id: self.db.lookup_intern_macro_call(call).def })
960963
.map(PathResolution::Macro),

crates/hir_def/src/path.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ impl Path {
9292
path: ModPath,
9393
generic_args: impl Into<Box<[Option<Interned<GenericArgs>>]>>,
9494
) -> Path {
95-
Path { type_anchor: None, mod_path: Interned::new(path), generic_args: generic_args.into() }
95+
let generic_args = generic_args.into();
96+
assert_eq!(path.len(), generic_args.len());
97+
Path { type_anchor: None, mod_path: Interned::new(path), generic_args }
9698
}
9799

98100
pub fn kind(&self) -> &PathKind {

crates/hir_ty/src/infer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ pub enum PointerCast {
253253
/// Go from a mut raw pointer to a const raw pointer.
254254
MutToConstPointer,
255255

256+
#[allow(dead_code)]
256257
/// Go from `*const [T; N]` to `*const T`
257258
ArrayToPointer,
258259

crates/ide_completion/src/completions.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub(crate) mod attribute;
44
pub(crate) mod dot;
55
pub(crate) mod flyimport;
66
pub(crate) mod fn_param;
7+
pub(crate) mod format_string;
78
pub(crate) mod keyword;
89
pub(crate) mod lifetime;
910
pub(crate) mod mod_;
@@ -14,7 +15,8 @@ pub(crate) mod record;
1415
pub(crate) mod snippet;
1516
pub(crate) mod trait_impl;
1617
pub(crate) mod unqualified_path;
17-
pub(crate) mod format_string;
18+
pub(crate) mod use_;
19+
pub(crate) mod vis;
1820

1921
use std::iter;
2022

@@ -97,6 +99,19 @@ impl Completions {
9799
item.add_to(self);
98100
}
99101

102+
pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext) {
103+
["self::", "super::", "crate::"].into_iter().for_each(|kw| self.add_keyword(ctx, kw));
104+
}
105+
106+
pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) {
107+
ctx.process_all_names(&mut |name, res| match res {
108+
ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {
109+
self.add_resolution(ctx, name, res);
110+
}
111+
_ => (),
112+
});
113+
}
114+
100115
pub(crate) fn add_resolution(
101116
&mut self,
102117
ctx: &CompletionContext,

crates/ide_completion/src/completions/attribute.rs

Lines changed: 85 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
//! Completion for attributes
1+
//! Completion for (built-in) attributes, derives and lints.
22
//!
3-
//! This module uses a bit of static metadata to provide completions
4-
//! for built-in attributes.
5-
//! Non-built-in attribute (excluding derives attributes) completions are done in [`super::unqualified_path`].
3+
//! This module uses a bit of static metadata to provide completions for builtin-in attributes and lints.
64
75
use ide_db::{
86
helpers::{
@@ -16,62 +14,107 @@ use ide_db::{
1614
use itertools::Itertools;
1715
use once_cell::sync::Lazy;
1816
use rustc_hash::FxHashMap;
19-
use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, SyntaxKind, T};
17+
use syntax::{
18+
ast::{self, AttrKind},
19+
AstNode, SyntaxKind, T,
20+
};
2021

21-
use crate::{context::CompletionContext, item::CompletionItem, Completions};
22+
use crate::{
23+
completions::module_or_attr,
24+
context::{CompletionContext, PathCompletionCtx, PathKind, PathQualifierCtx},
25+
item::CompletionItem,
26+
Completions,
27+
};
2228

2329
mod cfg;
2430
mod derive;
2531
mod lint;
2632
mod repr;
2733

28-
pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
34+
/// Complete inputs to known builtin attributes as well as derive attributes
35+
pub(crate) fn complete_known_attribute_input(
36+
acc: &mut Completions,
37+
ctx: &CompletionContext,
38+
) -> Option<()> {
2939
let attribute = ctx.fake_attribute_under_caret.as_ref()?;
3040
let name_ref = match attribute.path() {
3141
Some(p) => Some(p.as_single_name_ref()?),
3242
None => None,
3343
};
34-
match (name_ref, attribute.token_tree()) {
35-
(Some(path), Some(tt)) if tt.l_paren_token().is_some() => match path.text().as_str() {
36-
"repr" => repr::complete_repr(acc, ctx, tt),
37-
"derive" => derive::complete_derive(acc, ctx, ctx.attr.as_ref()?),
38-
"feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES),
39-
"allow" | "warn" | "deny" | "forbid" => {
40-
let existing_lints = parse_tt_as_comma_sep_paths(tt)?;
44+
let (path, tt) = name_ref.zip(attribute.token_tree())?;
45+
if tt.l_paren_token().is_none() {
46+
return None;
47+
}
4148

42-
let lints: Vec<Lint> = CLIPPY_LINT_GROUPS
43-
.iter()
44-
.map(|g| &g.lint)
45-
.chain(DEFAULT_LINTS.iter())
46-
.chain(CLIPPY_LINTS.iter())
47-
.chain(RUSTDOC_LINTS)
48-
.cloned()
49-
.collect();
49+
match path.text().as_str() {
50+
"repr" => repr::complete_repr(acc, ctx, tt),
51+
"derive" => derive::complete_derive(acc, ctx, ctx.attr.as_ref()?),
52+
"feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES),
53+
"allow" | "warn" | "deny" | "forbid" => {
54+
let existing_lints = parse_tt_as_comma_sep_paths(tt)?;
5055

51-
lint::complete_lint(acc, ctx, &existing_lints, &lints);
52-
}
53-
"cfg" => {
54-
cfg::complete_cfg(acc, ctx);
55-
}
56-
_ => (),
57-
},
58-
(_, Some(_)) => (),
59-
(_, None) if attribute.expr().is_some() => (),
60-
(_, None) => complete_new_attribute(acc, ctx, attribute),
56+
let lints: Vec<Lint> = CLIPPY_LINT_GROUPS
57+
.iter()
58+
.map(|g| &g.lint)
59+
.chain(DEFAULT_LINTS)
60+
.chain(CLIPPY_LINTS)
61+
.chain(RUSTDOC_LINTS)
62+
.cloned()
63+
.collect();
64+
65+
lint::complete_lint(acc, ctx, &existing_lints, &lints);
66+
}
67+
"cfg" => {
68+
cfg::complete_cfg(acc, ctx);
69+
}
70+
_ => (),
6171
}
6272
Some(())
6373
}
6474

65-
// FIXME?: Move this functionality to (un)qualified_path, make this module work solely for builtin/known attributes for their inputs?
66-
fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
67-
let is_inner = attribute.kind() == ast::AttrKind::Inner;
68-
let attribute_annotated_item_kind =
69-
attribute.syntax().parent().map(|it| it.kind()).filter(|_| {
70-
is_inner
71-
// If we got nothing coming after the attribute it could be anything so filter it the kind out
72-
|| non_trivia_sibling(attribute.syntax().clone().into(), Direction::Next).is_some()
73-
});
74-
let attributes = attribute_annotated_item_kind.and_then(|kind| {
75+
pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
76+
let (is_absolute_path, qualifier, is_inner, annotated_item_kind) = match ctx.path_context {
77+
Some(PathCompletionCtx {
78+
kind: Some(PathKind::Attr { kind, annotated_item_kind }),
79+
is_absolute_path,
80+
ref qualifier,
81+
..
82+
}) => (is_absolute_path, qualifier, kind == AttrKind::Inner, annotated_item_kind),
83+
_ => return,
84+
};
85+
86+
match qualifier {
87+
Some(PathQualifierCtx { resolution, is_super_chain, .. }) => {
88+
if *is_super_chain {
89+
acc.add_keyword(ctx, "super::");
90+
}
91+
92+
let module = match resolution {
93+
Some(hir::PathResolution::Def(hir::ModuleDef::Module(it))) => it,
94+
_ => return,
95+
};
96+
97+
for (name, def) in module.scope(ctx.db, ctx.module) {
98+
if let Some(def) = module_or_attr(def) {
99+
acc.add_resolution(ctx, name, def);
100+
}
101+
}
102+
return;
103+
}
104+
// fresh use tree with leading colon2, only show crate roots
105+
None if is_absolute_path => acc.add_crate_roots(ctx),
106+
// only show modules in a fresh UseTree
107+
None => {
108+
ctx.process_all_names(&mut |name, def| {
109+
if let Some(def) = module_or_attr(def) {
110+
acc.add_resolution(ctx, name, def);
111+
}
112+
});
113+
acc.add_nameref_keywords(ctx);
114+
}
115+
}
116+
117+
let attributes = annotated_item_kind.and_then(|kind| {
75118
if ast::Expr::can_cast(kind) {
76119
Some(EXPR_ATTRIBUTES)
77120
} else {

crates/ide_completion/src/completions/dot.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
3232
if !ctx.config.enable_self_on_the_fly {
3333
return;
3434
}
35-
if !ctx.is_trivial_path() || ctx.is_path_disallowed() || !ctx.expects_expression() {
35+
if ctx.is_non_trivial_path() || ctx.is_path_disallowed() || !ctx.expects_expression() {
3636
return;
3737
}
3838
if let Some(func) = ctx.function_def.as_ref().and_then(|fn_| ctx.sema.to_def(fn_)) {

crates/ide_completion/src/completions/flyimport.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
171171
(PathKind::Type, ItemInNs::Types(_)) => true,
172172
(PathKind::Type, ItemInNs::Values(_)) => false,
173173

174-
(PathKind::Attr, ItemInNs::Macros(mac)) => mac.is_attr(),
175-
(PathKind::Attr, _) => false,
174+
(PathKind::Attr { .. }, ItemInNs::Macros(mac)) => mac.is_attr(),
175+
(PathKind::Attr { .. }, _) => false,
176176
}
177177
};
178178

crates/ide_completion/src/completions/keyword.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use syntax::{SyntaxKind, T};
66

77
use crate::{
8-
context::{PathCompletionContext, PathKind},
8+
context::{PathCompletionCtx, PathKind},
99
patterns::ImmediateLocation,
1010
CompletionContext, CompletionItem, CompletionItemKind, Completions,
1111
};
@@ -27,18 +27,17 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
2727
cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
2828
return;
2929
}
30+
if ctx.pattern_ctx.is_some() {
31+
return;
32+
}
3033

3134
let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet);
3235

3336
let expects_assoc_item = ctx.expects_assoc_item();
3437
let has_block_expr_parent = ctx.has_block_expr_parent();
3538
let expects_item = ctx.expects_item();
3639

37-
if let Some(PathKind::Vis { has_in_token }) = ctx.path_kind() {
38-
if !has_in_token {
39-
cov_mark::hit!(kw_completion_in);
40-
add_keyword("in", "in");
41-
}
40+
if let Some(PathKind::Vis { .. }) = ctx.path_kind() {
4241
return;
4342
}
4443
if ctx.has_impl_or_trait_prev_sibling() {
@@ -121,14 +120,14 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
121120
add_keyword("else if", "else if $1 {\n $0\n}");
122121
}
123122

124-
if ctx.expects_ident_pat_or_ref_expr() {
123+
if ctx.expects_ident_ref_expr() {
125124
add_keyword("mut", "mut ");
126125
}
127126

128127
let (can_be_stmt, in_loop_body) = match ctx.path_context {
129-
Some(PathCompletionContext {
130-
is_trivial_path: true, can_be_stmt, in_loop_body, ..
131-
}) => (can_be_stmt, in_loop_body),
128+
Some(PathCompletionCtx { is_absolute_path: false, can_be_stmt, in_loop_body, .. }) => {
129+
(can_be_stmt, in_loop_body)
130+
}
132131
_ => return,
133132
};
134133

0 commit comments

Comments
 (0)