Unnamed repository; edit this file 'description' to name the repository.
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]>
bors[bot] 2022-02-03
parent 46b5089 · parent 7619c2a · commit 5a7e11f
-rw-r--r--crates/hir/src/display.rs2
-rw-r--r--crates/hir/src/lib.rs5
-rw-r--r--crates/hir/src/semantics.rs15
-rw-r--r--crates/hir_def/src/path.rs4
-rw-r--r--crates/hir_ty/src/infer.rs1
-rw-r--r--crates/ide_completion/src/completions.rs17
-rw-r--r--crates/ide_completion/src/completions/attribute.rs127
-rw-r--r--crates/ide_completion/src/completions/dot.rs2
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs4
-rw-r--r--crates/ide_completion/src/completions/keyword.rs19
-rw-r--r--crates/ide_completion/src/completions/pattern.rs133
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs116
-rw-r--r--crates/ide_completion/src/completions/snippet.rs6
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs45
-rw-r--r--crates/ide_completion/src/completions/use_.rs95
-rw-r--r--crates/ide_completion/src/completions/vis.rs52
-rw-r--r--crates/ide_completion/src/context.rs140
-rw-r--r--crates/ide_completion/src/lib.rs3
-rw-r--r--crates/ide_completion/src/render.rs4
-rw-r--r--crates/ide_completion/src/tests/attribute.rs144
-rw-r--r--crates/ide_completion/src/tests/fn_param.rs26
-rw-r--r--crates/ide_completion/src/tests/pattern.rs50
-rw-r--r--crates/ide_completion/src/tests/use_tree.rs38
-rw-r--r--crates/ide_db/src/search.rs6
-rw-r--r--crates/syntax/src/ast/node_ext.rs2
25 files changed, 700 insertions, 356 deletions
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index d94f55e326..1e949771ea 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -481,7 +481,7 @@ impl HirDisplay for Module {
// FIXME: Module doesn't have visibility saved in data.
match self.name(f.db) {
Some(name) => write!(f, "mod {}", name),
- None if self.crate_root(f.db) == *self => match self.krate().display_name(f.db) {
+ None if self.is_crate_root(f.db) => match self.krate().display_name(f.db) {
Some(name) => write!(f, "extern crate {}", name),
None => write!(f, "extern crate {{unknown}}"),
},
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 918cadc869..032da5f50a 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -452,6 +452,11 @@ impl Module {
Module { id: def_map.module_id(def_map.root()) }
}
+ pub fn is_crate_root(self, db: &dyn HirDatabase) -> bool {
+ let def_map = db.crate_def_map(self.id.krate());
+ def_map.root() == self.id.local_id
+ }
+
/// Iterates over all child modules.
pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> {
let def_map = self.id.def_map(db.upcast());
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 9ac88e260c..243ba63b8a 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -949,12 +949,15 @@ impl<'db> SemanticsImpl<'db> {
})?;
match res {
- Either::Left(path) => resolve_hir_path(
- self.db,
- &self.scope(derive.syntax()).resolver,
- &Path::from_known_path(path, []),
- )
- .filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))),
+ Either::Left(path) => {
+ let len = path.len();
+ resolve_hir_path(
+ self.db,
+ &self.scope(derive.syntax()).resolver,
+ &Path::from_known_path(path, vec![None; len]),
+ )
+ .filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_))))
+ }
Either::Right(derive) => derive
.map(|call| MacroDef { id: self.db.lookup_intern_macro_call(call).def })
.map(PathResolution::Macro),
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index fc81b88db3..a6141174c8 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -92,7 +92,9 @@ impl Path {
path: ModPath,
generic_args: impl Into<Box<[Option<Interned<GenericArgs>>]>>,
) -> Path {
- Path { type_anchor: None, mod_path: Interned::new(path), generic_args: generic_args.into() }
+ let generic_args = generic_args.into();
+ assert_eq!(path.len(), generic_args.len());
+ Path { type_anchor: None, mod_path: Interned::new(path), generic_args }
}
pub fn kind(&self) -> &PathKind {
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index a6c91f9d27..173380654e 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -253,6 +253,7 @@ pub enum PointerCast {
/// Go from a mut raw pointer to a const raw pointer.
MutToConstPointer,
+ #[allow(dead_code)]
/// Go from `*const [T; N]` to `*const T`
ArrayToPointer,
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index 92074b3e10..9c65efdb10 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -4,6 +4,7 @@ pub(crate) mod attribute;
pub(crate) mod dot;
pub(crate) mod flyimport;
pub(crate) mod fn_param;
+pub(crate) mod format_string;
pub(crate) mod keyword;
pub(crate) mod lifetime;
pub(crate) mod mod_;
@@ -14,7 +15,8 @@ pub(crate) mod record;
pub(crate) mod snippet;
pub(crate) mod trait_impl;
pub(crate) mod unqualified_path;
-pub(crate) mod format_string;
+pub(crate) mod use_;
+pub(crate) mod vis;
use std::iter;
@@ -97,6 +99,19 @@ impl Completions {
item.add_to(self);
}
+ pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext) {
+ ["self::", "super::", "crate::"].into_iter().for_each(|kw| self.add_keyword(ctx, kw));
+ }
+
+ pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) {
+ ctx.process_all_names(&mut |name, res| match res {
+ ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {
+ self.add_resolution(ctx, name, res);
+ }
+ _ => (),
+ });
+ }
+
pub(crate) fn add_resolution(
&mut self,
ctx: &CompletionContext,
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index 6b6759ebfd..cb45d9de03 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -1,8 +1,6 @@
-//! Completion for attributes
+//! Completion for (built-in) attributes, derives and lints.
//!
-//! This module uses a bit of static metadata to provide completions
-//! for built-in attributes.
-//! Non-built-in attribute (excluding derives attributes) completions are done in [`super::unqualified_path`].
+//! This module uses a bit of static metadata to provide completions for builtin-in attributes and lints.
use ide_db::{
helpers::{
@@ -16,62 +14,107 @@ use ide_db::{
use itertools::Itertools;
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
-use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, SyntaxKind, T};
+use syntax::{
+ ast::{self, AttrKind},
+ AstNode, SyntaxKind, T,
+};
-use crate::{context::CompletionContext, item::CompletionItem, Completions};
+use crate::{
+ completions::module_or_attr,
+ context::{CompletionContext, PathCompletionCtx, PathKind, PathQualifierCtx},
+ item::CompletionItem,
+ Completions,
+};
mod cfg;
mod derive;
mod lint;
mod repr;
-pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
+/// Complete inputs to known builtin attributes as well as derive attributes
+pub(crate) fn complete_known_attribute_input(
+ acc: &mut Completions,
+ ctx: &CompletionContext,
+) -> Option<()> {
let attribute = ctx.fake_attribute_under_caret.as_ref()?;
let name_ref = match attribute.path() {
Some(p) => Some(p.as_single_name_ref()?),
None => None,
};
- match (name_ref, attribute.token_tree()) {
- (Some(path), Some(tt)) if tt.l_paren_token().is_some() => match path.text().as_str() {
- "repr" => repr::complete_repr(acc, ctx, tt),
- "derive" => derive::complete_derive(acc, ctx, ctx.attr.as_ref()?),
- "feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES),
- "allow" | "warn" | "deny" | "forbid" => {
- let existing_lints = parse_tt_as_comma_sep_paths(tt)?;
+ let (path, tt) = name_ref.zip(attribute.token_tree())?;
+ if tt.l_paren_token().is_none() {
+ return None;
+ }
- let lints: Vec<Lint> = CLIPPY_LINT_GROUPS
- .iter()
- .map(|g| &g.lint)
- .chain(DEFAULT_LINTS.iter())
- .chain(CLIPPY_LINTS.iter())
- .chain(RUSTDOC_LINTS)
- .cloned()
- .collect();
+ match path.text().as_str() {
+ "repr" => repr::complete_repr(acc, ctx, tt),
+ "derive" => derive::complete_derive(acc, ctx, ctx.attr.as_ref()?),
+ "feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES),
+ "allow" | "warn" | "deny" | "forbid" => {
+ let existing_lints = parse_tt_as_comma_sep_paths(tt)?;
- lint::complete_lint(acc, ctx, &existing_lints, &lints);
- }
- "cfg" => {
- cfg::complete_cfg(acc, ctx);
- }
- _ => (),
- },
- (_, Some(_)) => (),
- (_, None) if attribute.expr().is_some() => (),
- (_, None) => complete_new_attribute(acc, ctx, attribute),
+ let lints: Vec<Lint> = CLIPPY_LINT_GROUPS
+ .iter()
+ .map(|g| &g.lint)
+ .chain(DEFAULT_LINTS)
+ .chain(CLIPPY_LINTS)
+ .chain(RUSTDOC_LINTS)
+ .cloned()
+ .collect();
+
+ lint::complete_lint(acc, ctx, &existing_lints, &lints);
+ }
+ "cfg" => {
+ cfg::complete_cfg(acc, ctx);
+ }
+ _ => (),
}
Some(())
}
-// FIXME?: Move this functionality to (un)qualified_path, make this module work solely for builtin/known attributes for their inputs?
-fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
- let is_inner = attribute.kind() == ast::AttrKind::Inner;
- let attribute_annotated_item_kind =
- attribute.syntax().parent().map(|it| it.kind()).filter(|_| {
- is_inner
- // If we got nothing coming after the attribute it could be anything so filter it the kind out
- || non_trivia_sibling(attribute.syntax().clone().into(), Direction::Next).is_some()
- });
- let attributes = attribute_annotated_item_kind.and_then(|kind| {
+pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
+ let (is_absolute_path, qualifier, is_inner, annotated_item_kind) = match ctx.path_context {
+ Some(PathCompletionCtx {
+ kind: Some(PathKind::Attr { kind, annotated_item_kind }),
+ is_absolute_path,
+ ref qualifier,
+ ..
+ }) => (is_absolute_path, qualifier, kind == AttrKind::Inner, annotated_item_kind),
+ _ => return,
+ };
+
+ match qualifier {
+ Some(PathQualifierCtx { resolution, is_super_chain, .. }) => {
+ if *is_super_chain {
+ acc.add_keyword(ctx, "super::");
+ }
+
+ let module = match resolution {
+ Some(hir::PathResolution::Def(hir::ModuleDef::Module(it))) => it,
+ _ => return,
+ };
+
+ for (name, def) in module.scope(ctx.db, ctx.module) {
+ if let Some(def) = module_or_attr(def) {
+ acc.add_resolution(ctx, name, def);
+ }
+ }
+ return;
+ }
+ // fresh use tree with leading colon2, only show crate roots
+ None if is_absolute_path => acc.add_crate_roots(ctx),
+ // only show modules in a fresh UseTree
+ None => {
+ ctx.process_all_names(&mut |name, def| {
+ if let Some(def) = module_or_attr(def) {
+ acc.add_resolution(ctx, name, def);
+ }
+ });
+ acc.add_nameref_keywords(ctx);
+ }
+ }
+
+ let attributes = annotated_item_kind.and_then(|kind| {
if ast::Expr::can_cast(kind) {
Some(EXPR_ATTRIBUTES)
} else {
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index b8bab94170..4a34b0f7e5 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -32,7 +32,7 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
if !ctx.config.enable_self_on_the_fly {
return;
}
- if !ctx.is_trivial_path() || ctx.is_path_disallowed() || !ctx.expects_expression() {
+ if ctx.is_non_trivial_path() || ctx.is_path_disallowed() || !ctx.expects_expression() {
return;
}
if let Some(func) = ctx.function_def.as_ref().and_then(|fn_| ctx.sema.to_def(fn_)) {
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 8ca4634be2..723abd62ae 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -171,8 +171,8 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
(PathKind::Type, ItemInNs::Types(_)) => true,
(PathKind::Type, ItemInNs::Values(_)) => false,
- (PathKind::Attr, ItemInNs::Macros(mac)) => mac.is_attr(),
- (PathKind::Attr, _) => false,
+ (PathKind::Attr { .. }, ItemInNs::Macros(mac)) => mac.is_attr(),
+ (PathKind::Attr { .. }, _) => false,
}
};
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 6b013c9122..4704e842e6 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -5,7 +5,7 @@
use syntax::{SyntaxKind, T};
use crate::{
- context::{PathCompletionContext, PathKind},
+ context::{PathCompletionCtx, PathKind},
patterns::ImmediateLocation,
CompletionContext, CompletionItem, CompletionItemKind, Completions,
};
@@ -27,6 +27,9 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
return;
}
+ if ctx.pattern_ctx.is_some() {
+ return;
+ }
let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet);
@@ -34,11 +37,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
let has_block_expr_parent = ctx.has_block_expr_parent();
let expects_item = ctx.expects_item();
- if let Some(PathKind::Vis { has_in_token }) = ctx.path_kind() {
- if !has_in_token {
- cov_mark::hit!(kw_completion_in);
- add_keyword("in", "in");
- }
+ if let Some(PathKind::Vis { .. }) = ctx.path_kind() {
return;
}
if ctx.has_impl_or_trait_prev_sibling() {
@@ -121,14 +120,14 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
add_keyword("else if", "else if $1 {\n $0\n}");
}
- if ctx.expects_ident_pat_or_ref_expr() {
+ if ctx.expects_ident_ref_expr() {
add_keyword("mut", "mut ");
}
let (can_be_stmt, in_loop_body) = match ctx.path_context {
- Some(PathCompletionContext {
- is_trivial_path: true, can_be_stmt, in_loop_body, ..
- }) => (can_be_stmt, in_loop_body),
+ Some(PathCompletionCtx { is_absolute_path: false, can_be_stmt, in_loop_body, .. }) => {
+ (can_be_stmt, in_loop_body)
+ }
_ => return,
};
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index a140ca4239..f1b4fa7205 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -1,20 +1,52 @@
//! Completes constants and paths in unqualified patterns.
-use hir::db::DefDatabase;
+use hir::{db::DefDatabase, AssocItem, ScopeDef};
+use rustc_hash::FxHashSet;
+use syntax::ast::Pat;
use crate::{
- context::{PatternContext, PatternRefutability},
+ context::{PathCompletionCtx, PathQualifierCtx, PatternRefutability},
CompletionContext, Completions,
};
/// Completes constants and paths in unqualified patterns.
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
- let refutable = match ctx.pattern_ctx {
- Some(PatternContext { refutability, .. }) if ctx.path_context.is_none() => {
- refutability == PatternRefutability::Refutable
- }
+ let patctx = match &ctx.pattern_ctx {
+ Some(ctx) => ctx,
_ => return,
};
+ let refutable = patctx.refutability == PatternRefutability::Refutable;
+
+ if let Some(path_ctx) = &ctx.path_context {
+ pattern_path_completion(acc, ctx, path_ctx);
+ return;
+ }
+
+ match patctx.parent_pat.as_ref() {
+ Some(Pat::RangePat(_) | Pat::BoxPat(_)) => (),
+ Some(Pat::RefPat(r)) => {
+ if r.mut_token().is_none() {
+ acc.add_keyword(ctx, "mut");
+ }
+ }
+ _ => {
+ let tok = ctx.token.text_range().start();
+ match (patctx.ref_token.as_ref(), patctx.mut_token.as_ref()) {
+ (None, None) => {
+ acc.add_keyword(ctx, "ref");
+ acc.add_keyword(ctx, "mut");
+ }
+ (None, Some(m)) if tok < m.text_range().start() => {
+ acc.add_keyword(ctx, "ref");
+ }
+ (Some(r), None) if tok > r.text_range().end() => {
+ acc.add_keyword(ctx, "mut");
+ }
+ _ => (),
+ }
+ }
+ }
+
let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
if let Some(hir::Adt::Enum(e)) =
@@ -63,3 +95,92 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
}
});
}
+
+fn pattern_path_completion(
+ acc: &mut Completions,
+ ctx: &CompletionContext,
+ PathCompletionCtx { qualifier, is_absolute_path, .. }: &PathCompletionCtx,
+) {
+ match qualifier {
+ Some(PathQualifierCtx { resolution, is_super_chain, .. }) => {
+ if *is_super_chain {
+ acc.add_keyword(ctx, "super::");
+ }
+
+ let resolution = match resolution {
+ Some(it) => it,
+ None => return,
+ };
+
+ match resolution {
+ hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
+ let module_scope = module.scope(ctx.db, ctx.module);
+ for (name, def) in module_scope {
+ let add_resolution = match def {
+ ScopeDef::MacroDef(m) if m.is_fn_like() => true,
+ ScopeDef::ModuleDef(_) => true,
+ _ => false,
+ };
+
+ if add_resolution {
+ acc.add_resolution(ctx, name, def);
+ }
+ }
+ }
+ hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
+ cov_mark::hit!(enum_plain_qualified_use_tree);
+ e.variants(ctx.db)
+ .into_iter()
+ .for_each(|variant| acc.add_enum_variant(ctx, variant, None));
+ }
+ res @ (hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_)) => {
+ if let Some(krate) = ctx.krate {
+ let ty = match res {
+ hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
+ hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
+ _ => return,
+ };
+
+ if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
+ e.variants(ctx.db)
+ .into_iter()
+ .for_each(|variant| acc.add_enum_variant(ctx, variant, None));
+ }
+
+ let traits_in_scope = ctx.scope.visible_traits();
+ let mut seen = FxHashSet::default();
+ ty.iterate_path_candidates(
+ ctx.db,
+ krate,
+ &traits_in_scope,
+ ctx.module,
+ None,
+ |_ty, item| {
+ // Note associated consts cannot be referenced in patterns
+ if let AssocItem::TypeAlias(ta) = item {
+ // We might iterate candidates of a trait multiple times here, so deduplicate them.
+ if seen.insert(item) {
+ acc.add_type_alias(ctx, ta);
+ }
+ }
+ None::<()>
+ },
+ );
+ }
+ }
+ _ => {}
+ }
+ }
+ // qualifier can only be none here if we are in a TuplePat or RecordPat in which case special characters have to follow the path
+ None if *is_absolute_path => acc.add_crate_roots(ctx),
+ None => {
+ cov_mark::hit!(unqualified_path_only_modules_in_import);
+ ctx.process_all_names(&mut |name, res| {
+ if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
+ acc.add_resolution(ctx, name, res);
+ }
+ });
+ acc.add_nameref_keywords(ctx);
+ }
+ }
+}
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 2c6899ff0c..cf78f7c1ad 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -1,14 +1,12 @@
//! Completion of paths, i.e. `some::prefix::$0`.
-use std::iter;
-
use hir::{ScopeDef, Trait};
use rustc_hash::FxHashSet;
-use syntax::{ast, AstNode};
+use syntax::ast;
use crate::{
- completions::{module_or_attr, module_or_fn_macro},
- context::{PathCompletionContext, PathKind},
+ completions::module_or_fn_macro,
+ context::{PathCompletionCtx, PathKind},
patterns::ImmediateLocation,
CompletionContext, Completions,
};
@@ -17,21 +15,19 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
return;
}
- let (path, use_tree_parent, kind) = match ctx.path_context {
+ if ctx.pattern_ctx.is_some() {
+ return;
+ }
+ let (qualifier, kind) = match ctx.path_context {
// let ... else, syntax would come in really handy here right now
- Some(PathCompletionContext {
- qualifier: Some(ref qualifier),
- use_tree_parent,
- kind,
- ..
- }) => (qualifier, use_tree_parent, kind),
+ Some(PathCompletionCtx { qualifier: Some(ref qualifier), kind, .. }) => (qualifier, kind),
_ => return,
};
// special case `<_>::$0` as this doesn't resolve to anything.
- if path.qualifier().is_none() {
+ if qualifier.path.qualifier().is_none() {
if matches!(
- path.segment().and_then(|it| it.kind()),
+ qualifier.path.segment().and_then(|it| it.kind()),
Some(ast::PathSegmentKind::Type {
type_ref: Some(ast::Type::InferType(_)),
trait_ref: None,
@@ -47,17 +43,15 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
}
}
- let resolution = match ctx.sema.resolve_path(path) {
+ let resolution = match &qualifier.resolution {
Some(res) => res,
None => return,
};
- let context_module = ctx.module;
-
match ctx.completion_location {
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
- for (name, def) in module.scope(ctx.db, context_module) {
+ for (name, def) in module.scope(ctx.db, ctx.module) {
if let Some(def) = module_or_fn_macro(def) {
acc.add_resolution(ctx, name, def);
}
@@ -69,78 +63,22 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
}
match kind {
- // Complete next child module that comes after the qualified module which is still our parent
- Some(PathKind::Vis { .. }) => {
- if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
- if let Some(current_module) = ctx.module {
- let next_towards_current = current_module
- .path_to_root(ctx.db)
- .into_iter()
- .take_while(|&it| it != module)
- .next();
- if let Some(next) = next_towards_current {
- if let Some(name) = next.name(ctx.db) {
- cov_mark::hit!(visibility_qualified);
- acc.add_resolution(ctx, name, ScopeDef::ModuleDef(next.into()));
- }
- }
- }
- }
- return;
- }
- Some(PathKind::Attr) => {
- if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
- for (name, def) in module.scope(ctx.db, context_module) {
- if let Some(def) = module_or_attr(def) {
- acc.add_resolution(ctx, name, def);
- }
- }
- }
+ Some(PathKind::Pat | PathKind::Attr { .. } | PathKind::Vis { .. } | PathKind::Use) => {
return;
}
- Some(PathKind::Use) => {
- if iter::successors(Some(path.clone()), |p| p.qualifier())
- .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
- {
- acc.add_keyword(ctx, "super::");
- }
- // only show `self` in a new use-tree when the qualifier doesn't end in self
- if use_tree_parent
- && !matches!(
- path.segment().and_then(|it| it.kind()),
- Some(ast::PathSegmentKind::SelfKw)
- )
- {
- acc.add_keyword(ctx, "self");
- }
+ _ => {
+ // Add associated types on type parameters and `Self`.
+ ctx.scope.assoc_type_shorthand_candidates(&resolution, |_, alias| {
+ acc.add_type_alias(ctx, alias);
+ None::<()>
+ });
}
- _ => (),
- }
-
- if !matches!(kind, Some(PathKind::Pat)) {
- // Add associated types on type parameters and `Self`.
- ctx.scope.assoc_type_shorthand_candidates(&resolution, |_, alias| {
- acc.add_type_alias(ctx, alias);
- None::<()>
- });
}
match resolution {
hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
- let module_scope = module.scope(ctx.db, context_module);
+ let module_scope = module.scope(ctx.db, ctx.module);
for (name, def) in module_scope {
- if let Some(PathKind::Use) = kind {
- if let ScopeDef::Unknown = def {
- if let Some(ast::NameLike::NameRef(name_ref)) = ctx.name_syntax.as_ref() {
- if name_ref.syntax().text() == name.to_smol_str().as_str() {
- // for `use self::foo$0`, don't suggest `foo` as a completion
- cov_mark::hit!(dont_complete_current_use);
- continue;
- }
- }
- }
- }
-
let add_resolution = match def {
// Don't suggest attribute macros and derives.
ScopeDef::MacroDef(mac) => mac.is_fn_like(),
@@ -168,7 +106,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
| hir::ModuleDef::TypeAlias(_)
| hir::ModuleDef::BuiltinType(_)),
) => {
- if let hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def {
+ if let &hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def {
add_enum_variants(acc, ctx, e);
}
let ty = match def {
@@ -623,18 +561,6 @@ fn foo() {
}
#[test]
- fn dont_complete_attr() {
- check(
- r#"
-mod foo { pub struct Foo; }
-#[foo::$0]
-fn f() {}
-"#,
- expect![[""]],
- );
- }
-
- #[test]
fn completes_variant_through_self() {
check(
r#"
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index 02f711f51d..9a2b9c2fa4 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -5,7 +5,7 @@ use ide_db::helpers::{insert_use::ImportScope, SnippetCap};
use syntax::T;
use crate::{
- context::PathCompletionContext, item::Builder, CompletionContext, CompletionItem,
+ context::PathCompletionCtx, item::Builder, CompletionContext, CompletionItem,
CompletionItemKind, Completions, SnippetScope,
};
@@ -21,7 +21,9 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
}
let can_be_stmt = match ctx.path_context {
- Some(PathCompletionContext { is_trivial_path: true, can_be_stmt, .. }) => can_be_stmt,
+ Some(PathCompletionCtx {
+ is_absolute_path: false, qualifier: None, can_be_stmt, ..
+ }) => can_be_stmt,
_ => return,
};
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 7e06b074ce..cca2785e2d 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -4,8 +4,8 @@ use hir::ScopeDef;
use syntax::{ast, AstNode};
use crate::{
- completions::{module_or_attr, module_or_fn_macro},
- context::{PathCompletionContext, PathKind},
+ completions::module_or_fn_macro,
+ context::{PathCompletionCtx, PathKind},
patterns::ImmediateLocation,
CompletionContext, Completions,
};
@@ -15,37 +15,22 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
return;
}
- let kind = match ctx.path_context {
- Some(PathCompletionContext { is_trivial_path: true, kind, .. }) => kind,
+ match ctx.path_context {
+ Some(PathCompletionCtx {
+ kind:
+ Some(
+ PathKind::Vis { .. }
+ | PathKind::Attr { .. }
+ | PathKind::Use { .. }
+ | PathKind::Pat,
+ ),
+ ..
+ }) => return,
+ Some(PathCompletionCtx { is_absolute_path: false, qualifier: None, .. }) => (),
_ => return,
- };
-
- if let Some(PathKind::Use) = kind {
- // only show modules in a fresh UseTree
- cov_mark::hit!(unqualified_path_only_modules_in_import);
- ctx.process_all_names(&mut |name, res| {
- if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
- acc.add_resolution(ctx, name, res);
- }
- });
-
- ["self::", "super::", "crate::"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
- return;
}
- ["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
- match kind {
- Some(PathKind::Vis { .. }) => return,
- Some(PathKind::Attr) => {
- ctx.process_all_names(&mut |name, def| {
- if let Some(def) = module_or_attr(def) {
- acc.add_resolution(ctx, name, def);
- }
- });
- return;
- }
- _ => (),
- }
+ ["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
match &ctx.completion_location {
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
diff --git a/crates/ide_completion/src/completions/use_.rs b/crates/ide_completion/src/completions/use_.rs
new file mode 100644
index 0000000000..6f980845c7
--- /dev/null
+++ b/crates/ide_completion/src/completions/use_.rs
@@ -0,0 +1,95 @@
+//! Completion for use trees
+
+use hir::ScopeDef;
+use syntax::{ast, AstNode};
+
+use crate::{
+ context::{CompletionContext, PathCompletionCtx, PathKind, PathQualifierCtx},
+ Completions,
+};
+
+pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) {
+ let (is_absolute_path, qualifier) = match ctx.path_context {
+ Some(PathCompletionCtx {
+ kind: Some(PathKind::Use),
+ is_absolute_path,
+ ref qualifier,
+ ..
+ }) => (is_absolute_path, qualifier),
+ _ => return,
+ };
+
+ match qualifier {
+ Some(PathQualifierCtx { path, resolution, is_super_chain, use_tree_parent }) => {
+ if *is_super_chain {
+ acc.add_keyword(ctx, "super::");
+ }
+ // only show `self` in a new use-tree when the qualifier doesn't end in self
+ let not_preceded_by_self = *use_tree_parent
+ && !matches!(
+ path.segment().and_then(|it| it.kind()),
+ Some(ast::PathSegmentKind::SelfKw)
+ );
+ if not_preceded_by_self {
+ acc.add_keyword(ctx, "self");
+ }
+
+ let resolution = match resolution {
+ Some(it) => it,
+ None => return,
+ };
+
+ match resolution {
+ hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
+ let module_scope = module.scope(ctx.db, ctx.module);
+ let unknown_is_current = |name: &hir::Name| {
+ matches!(
+ ctx.name_syntax.as_ref(),
+ Some(ast::NameLike::NameRef(name_ref))
+ if name_ref.syntax().text() == name.to_smol_str().as_str()
+ )
+ };
+ for (name, def) in module_scope {
+ let add_resolution = match def {
+ ScopeDef::Unknown if unknown_is_current(&name) => {
+ // for `use self::foo$0`, don't suggest `foo` as a completion
+ cov_mark::hit!(dont_complete_current_use);
+ continue;
+ }
+ ScopeDef::ModuleDef(_) | ScopeDef::MacroDef(_) | ScopeDef::Unknown => {
+ true
+ }
+ _ => false,
+ };
+
+ if add_resolution {
+ acc.add_resolution(ctx, name, def);
+ }
+ }
+ }
+ hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
+ cov_mark::hit!(enum_plain_qualified_use_tree);
+ e.variants(ctx.db)
+ .into_iter()
+ .for_each(|variant| acc.add_enum_variant(ctx, variant, None));
+ }
+ _ => {}
+ }
+ }
+ // fresh use tree with leading colon2, only show crate roots
+ None if is_absolute_path => {
+ cov_mark::hit!(use_tree_crate_roots_only);
+ acc.add_crate_roots(ctx);
+ }
+ // only show modules in a fresh UseTree
+ None => {
+ cov_mark::hit!(unqualified_path_only_modules_in_import);
+ ctx.process_all_names(&mut |name, res| {
+ if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
+ acc.add_resolution(ctx, name, res);
+ }
+ });
+ acc.add_nameref_keywords(ctx);
+ }
+ }
+}
diff --git a/crates/ide_completion/src/completions/vis.rs b/crates/ide_completion/src/completions/vis.rs
new file mode 100644
index 0000000000..9cf9632658
--- /dev/null
+++ b/crates/ide_completion/src/completions/vis.rs
@@ -0,0 +1,52 @@
+//! Completion for visibility specifiers.
+
+use hir::ScopeDef;
+
+use crate::{
+ context::{CompletionContext, PathCompletionCtx, PathKind, PathQualifierCtx},
+ Completions,
+};
+
+pub(crate) fn complete_vis(acc: &mut Completions, ctx: &CompletionContext) {
+ let (is_absolute_path, qualifier, has_in_token) = match ctx.path_context {
+ Some(PathCompletionCtx {
+ kind: Some(PathKind::Vis { has_in_token }),
+ is_absolute_path,
+ ref qualifier,
+ ..
+ }) => (is_absolute_path, qualifier, has_in_token),
+ _ => return,
+ };
+
+ match qualifier {
+ Some(PathQualifierCtx { resolution, is_super_chain, .. }) => {
+ if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = resolution {
+ if let Some(current_module) = ctx.module {
+ let next_towards_current = current_module
+ .path_to_root(ctx.db)
+ .into_iter()
+ .take_while(|it| it != module)
+ .next();
+ if let Some(next) = next_towards_current {
+ if let Some(name) = next.name(ctx.db) {
+ cov_mark::hit!(visibility_qualified);
+ acc.add_resolution(ctx, name, ScopeDef::ModuleDef(next.into()));
+ }
+ }
+ }
+ }
+
+ if *is_super_chain {
+ acc.add_keyword(ctx, "super::");
+ }
+ }
+ None if !is_absolute_path => {
+ if !has_in_token {
+ cov_mark::hit!(kw_completion_in);
+ acc.add_keyword(ctx, "in");
+ }
+ ["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
+ }
+ _ => {}
+ }
+}
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index ab55d9cc04..d711215491 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -3,7 +3,9 @@
use std::iter;
use base_db::SourceDatabaseExt;
-use hir::{HasAttrs, Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo};
+use hir::{
+ HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
+};
use ide_db::{
active_parameter::ActiveParameter,
base_db::{FilePosition, SourceDatabase},
@@ -11,8 +13,8 @@ use ide_db::{
RootDatabase,
};
use syntax::{
- algo::find_node_at_offset,
- ast::{self, HasName, NameOrNameRef},
+ algo::{find_node_at_offset, non_trivia_sibling},
+ ast::{self, AttrKind, HasName, NameOrNameRef},
match_ast, AstNode, NodeOrToken,
SyntaxKind::{self, *},
SyntaxNode, SyntaxToken, TextRange, TextSize, T,
@@ -34,6 +36,7 @@ pub(crate) enum PatternRefutability {
Refutable,
Irrefutable,
}
+
pub(crate) enum Visible {
Yes,
Editable,
@@ -44,7 +47,7 @@ pub(crate) enum Visible {
pub(super) enum PathKind {
Expr,
Type,
- Attr,
+ Attr { kind: AttrKind, annotated_item_kind: Option<SyntaxKind> },
Mac,
Pat,
Vis { has_in_token: bool },
@@ -52,18 +55,13 @@ pub(super) enum PathKind {
}
#[derive(Debug)]
-pub(crate) struct PathCompletionContext {
+pub(crate) struct PathCompletionCtx {
/// If this is a call with () already there
has_call_parens: bool,
- /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
- pub(super) is_trivial_path: bool,
- /// If not a trivial path, the prefix (qualifier).
- pub(super) qualifier: Option<ast::Path>,
- #[allow(dead_code)]
- /// If not a trivial path, the suffix (parent).
- pub(super) parent: Option<ast::Path>,
- /// Whether the qualifier comes from a use tree parent or not
- pub(super) use_tree_parent: bool,
+ /// Whether this path stars with a `::`.
+ pub(super) is_absolute_path: bool,
+ /// The qualifier of the current path if it exists.
+ pub(super) qualifier: Option<PathQualifierCtx>,
pub(super) kind: Option<PathKind>,
/// Whether the path segment has type args or not.
pub(super) has_type_args: bool,
@@ -73,10 +71,23 @@ pub(crate) struct PathCompletionContext {
}
#[derive(Debug)]
+pub(crate) struct PathQualifierCtx {
+ pub(crate) path: ast::Path,
+ pub(crate) resolution: Option<PathResolution>,
+ /// Whether this path consists solely of `super` segments
+ pub(crate) is_super_chain: bool,
+ /// Whether the qualifier comes from a use tree parent or not
+ pub(crate) use_tree_parent: bool,
+}
+
+#[derive(Debug)]
pub(super) struct PatternContext {
pub(super) refutability: PatternRefutability,
pub(super) param_ctx: Option<(ast::ParamList, ast::Param, ParamKind)>,
pub(super) has_type_ascription: bool,
+ pub(super) parent_pat: Option<ast::Pat>,
+ pub(super) ref_token: Option<SyntaxToken>,
+ pub(super) mut_token: Option<SyntaxToken>,
}
#[derive(Debug)]
@@ -129,7 +140,7 @@ pub(crate) struct CompletionContext<'a> {
pub(super) lifetime_ctx: Option<LifetimeContext>,
pub(super) pattern_ctx: Option<PatternContext>,
- pub(super) path_context: Option<PathCompletionContext>,
+ pub(super) path_context: Option<PathCompletionCtx>,
pub(super) locals: Vec<(Name, Local)>,
@@ -211,11 +222,8 @@ impl<'a> CompletionContext<'a> {
matches!(self.completion_location, Some(ImmediateLocation::StmtList))
}
- pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool {
- matches!(
- self.completion_location,
- Some(ImmediateLocation::IdentPat | ImmediateLocation::RefExpr)
- )
+ pub(crate) fn expects_ident_ref_expr(&self) -> bool {
+ matches!(self.completion_location, Some(ImmediateLocation::RefExpr))
}
pub(crate) fn expect_field(&self) -> bool {
@@ -262,27 +270,29 @@ impl<'a> CompletionContext<'a> {
}
pub(crate) fn expects_expression(&self) -> bool {
- matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Expr), .. }))
+ matches!(self.path_context, Some(PathCompletionCtx { kind: Some(PathKind::Expr), .. }))
}
pub(crate) fn expects_type(&self) -> bool {
- matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), .. }))
+ matches!(self.path_context, Some(PathCompletionCtx { kind: Some(PathKind::Type), .. }))
}
pub(crate) fn path_is_call(&self) -> bool {
self.path_context.as_ref().map_or(false, |it| it.has_call_parens)
}
- pub(crate) fn is_trivial_path(&self) -> bool {
- matches!(self.path_context, Some(PathCompletionContext { is_trivial_path: true, .. }))
- }
-
pub(crate) fn is_non_trivial_path(&self) -> bool {
- matches!(self.path_context, Some(PathCompletionContext { is_trivial_path: false, .. }))
+ matches!(
+ self.path_context,
+ Some(
+ PathCompletionCtx { is_absolute_path: true, .. }
+ | PathCompletionCtx { qualifier: Some(_), .. }
+ )
+ )
}
pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
- self.path_context.as_ref().and_then(|it| it.qualifier.as_ref())
+ self.path_context.as_ref().and_then(|it| it.qualifier.as_ref().map(|it| &it.path))
}
pub(crate) fn path_kind(&self) -> Option<PathKind> {
@@ -779,37 +789,33 @@ impl<'a> CompletionContext<'a> {
if is_name_in_field_pat {
return None;
}
- if !bind_pat.is_simple_ident() {
- return None;
- }
Some(pattern_context_for(original_file, bind_pat.into()))
}
fn classify_name_ref(
- _sema: &Semantics<RootDatabase>,
+ sema: &Semantics<RootDatabase>,
original_file: &SyntaxNode,
name_ref: ast::NameRef,
- ) -> Option<(PathCompletionContext, Option<PatternContext>)> {
+ ) -> Option<(PathCompletionCtx, Option<PatternContext>)> {
let parent = name_ref.syntax().parent()?;
let segment = ast::PathSegment::cast(parent)?;
let path = segment.parent_path();
- let mut path_ctx = PathCompletionContext {
+ let mut path_ctx = PathCompletionCtx {
has_call_parens: false,
- is_trivial_path: false,
+ is_absolute_path: false,
qualifier: None,
- parent: None,
has_type_args: false,
can_be_stmt: false,
in_loop_body: false,
- use_tree_parent: false,
kind: None,
};
let mut pat_ctx = None;
path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax());
- path_ctx.kind = path.syntax().ancestors().find_map(|it| {
- match_ast! {
+ path_ctx.kind = path.syntax().ancestors().find_map(|it| {
+ // using Option<Option<PathKind>> as extra controlflow
+ let kind = match_ast! {
match it {
ast::PathType(_) => Some(PathKind::Type),
ast::PathExpr(it) => {
@@ -830,32 +836,57 @@ impl<'a> CompletionContext<'a> {
Some(PathKind::Pat)
},
ast::MacroCall(it) => it.excl_token().and(Some(PathKind::Mac)),
- ast::Meta(_) => Some(PathKind::Attr),
+ ast::Meta(meta) => (|| {
+ let attr = meta.parent_attr()?;
+ let kind = attr.kind();
+ let attached = attr.syntax().parent()?;
+ let is_trailing_outer_attr = kind != AttrKind::Inner
+ && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
+ let annotated_item_kind = if is_trailing_outer_attr {
+ None
+ } else {
+ Some(attached.kind())
+ };
+ Some(PathKind::Attr {
+ kind,
+ annotated_item_kind,
+ })
+ })(),
ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
ast::UseTree(_) => Some(PathKind::Use),
- _ => None,
+ _ => return None,
}
- }
- });
+ };
+ Some(kind)
+ }).flatten();
path_ctx.has_type_args = segment.generic_arg_list().is_some();
if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
- path_ctx.use_tree_parent = use_tree_parent;
- path_ctx.qualifier = path
+ if !use_tree_parent {
+ path_ctx.is_absolute_path =
+ path.top_path().segment().map_or(false, |it| it.coloncolon_token().is_some());
+ }
+
+ let path = path
.segment()
.and_then(|it| find_node_in_file(original_file, &it))
.map(|it| it.parent_path());
+ path_ctx.qualifier = path.map(|path| {
+ let res = sema.resolve_path(&path);
+ let is_super_chain = iter::successors(Some(path.clone()), |p| p.qualifier())
+ .all(|p| p.segment().and_then(|s| s.super_token()).is_some());
+ PathQualifierCtx { path, resolution: res, is_super_chain, use_tree_parent }
+ });
return Some((path_ctx, pat_ctx));
}
if let Some(segment) = path.segment() {
if segment.coloncolon_token().is_some() {
+ path_ctx.is_absolute_path = true;
return Some((path_ctx, pat_ctx));
}
}
- path_ctx.is_trivial_path = true;
-
// Find either enclosing expr statement (thing with `;`) or a
// block. If block, check that we are the last expr.
path_ctx.can_be_stmt = name_ref
@@ -915,7 +946,18 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont
};
(refutability, false)
});
- PatternContext { refutability, param_ctx: is_param, has_type_ascription }
+ let (ref_token, mut_token) = match &pat {
+ ast::Pat::IdentPat(it) => (it.ref_token(), it.mut_token()),
+ _ => (None, None),
+ };
+ PatternContext {
+ refutability,
+ param_ctx: is_param,
+ has_type_ascription,
+ parent_pat: pat.syntax().parent().and_then(ast::Pat::cast),
+ mut_token,
+ ref_token,
+ }
}
fn find_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
@@ -946,7 +988,7 @@ fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
}
let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?;
- use_tree.path().zip(Some(true))
+ Some((use_tree.path()?, true))
}
fn has_ref(token: &SyntaxToken) -> bool {
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index a2217af493..f0cd125973 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -151,7 +151,10 @@ pub fn completions(
}
let mut acc = Completions::default();
+ completions::attribute::complete_known_attribute_input(&mut acc, &ctx);
completions::attribute::complete_attribute(&mut acc, &ctx);
+ completions::use_::complete_use_tree(&mut acc, &ctx);
+ completions::vis::complete_vis(&mut acc, &ctx);
completions::fn_param::complete_fn_param(&mut acc, &ctx);
completions::keyword::complete_expr_keyword(&mut acc, &ctx);
completions::snippet::complete_expr_snippet(&mut acc, &ctx);
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 6be2651123..027fb53527 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -19,7 +19,7 @@ use ide_db::{
use syntax::{SmolStr, SyntaxKind, TextRange};
use crate::{
- context::{PathCompletionContext, PathKind},
+ context::{PathCompletionCtx, PathKind},
item::{CompletionRelevanceTypeMatch, ImportEdit},
render::{enum_variant::render_variant, function::render_fn, macro_::render_macro},
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
@@ -234,7 +234,7 @@ fn render_resolution_(
// Add `<>` for generic types
let type_path_no_ty_args = matches!(
ctx.completion.path_context,
- Some(PathCompletionContext { kind: Some(PathKind::Type), has_type_args: false, .. })
+ Some(PathCompletionCtx { kind: Some(PathKind::Type), has_type_args: false, .. })
) && ctx.completion.config.add_call_parenthesis;
if type_path_no_ty_args {
if let Some(cap) = ctx.snippet_cap() {
diff --git a/crates/ide_completion/src/tests/attribute.rs b/crates/ide_completion/src/tests/attribute.rs
index 2d2a1b867a..5a9c48a327 100644
--- a/crates/ide_completion/src/tests/attribute.rs
+++ b/crates/ide_completion/src/tests/attribute.rs
@@ -17,6 +17,10 @@ fn proc_macros() {
struct Foo;
"#,
expect![[r#"
+ md proc_macros
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -32,10 +36,6 @@ struct Foo;
at derive(…)
at repr(…)
at non_exhaustive
- kw self
- kw super
- kw crate
- md proc_macros
"#]],
)
}
@@ -78,15 +78,15 @@ fn with_existing_attr() {
check(
r#"#[no_mangle] #[$0] mcall!();"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
at deny(…)
at forbid(…)
at warn(…)
- kw self
- kw super
- kw crate
"#]],
)
}
@@ -96,6 +96,9 @@ fn attr_on_source_file() {
check(
r#"#![$0]"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -116,9 +119,6 @@ fn attr_on_source_file() {
at recursion_limit = "…"
at type_length_limit = …
at windows_subsystem = "…"
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -128,6 +128,9 @@ fn attr_on_module() {
check(
r#"#[$0] mod foo;"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -142,14 +145,14 @@ fn attr_on_module() {
at no_mangle
at macro_use
at path = "…"
- kw self
- kw super
- kw crate
"#]],
);
check(
r#"mod foo {#![$0]}"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -163,9 +166,6 @@ fn attr_on_module() {
at must_use
at no_mangle
at no_implicit_prelude
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -175,6 +175,9 @@ fn attr_on_macro_rules() {
check(
r#"#[$0] macro_rules! foo {}"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -189,9 +192,6 @@ fn attr_on_macro_rules() {
at no_mangle
at macro_export
at macro_use
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -201,6 +201,9 @@ fn attr_on_macro_def() {
check(
r#"#[$0] macro foo {}"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -213,9 +216,6 @@ fn attr_on_macro_def() {
at doc(alias = "…")
at must_use
at no_mangle
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -225,6 +225,9 @@ fn attr_on_extern_crate() {
check(
r#"#[$0] extern crate foo;"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -238,9 +241,6 @@ fn attr_on_extern_crate() {
at must_use
at no_mangle
at macro_use
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -250,6 +250,9 @@ fn attr_on_use() {
check(
r#"#[$0] use foo;"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -262,9 +265,6 @@ fn attr_on_use() {
at doc(alias = "…")
at must_use
at no_mangle
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -274,6 +274,9 @@ fn attr_on_type_alias() {
check(
r#"#[$0] type foo = ();"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -286,9 +289,6 @@ fn attr_on_type_alias() {
at doc(alias = "…")
at must_use
at no_mangle
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -302,6 +302,11 @@ fn attr_on_struct() {
struct Foo;
"#,
expect![[r#"
+ md core
+ at derive pub macro derive
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -317,11 +322,6 @@ struct Foo;
at derive(…)
at repr(…)
at non_exhaustive
- kw self
- kw super
- kw crate
- md core
- at derive pub macro derive
"#]],
);
}
@@ -331,6 +331,9 @@ fn attr_on_enum() {
check(
r#"#[$0] enum Foo {}"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -346,9 +349,6 @@ fn attr_on_enum() {
at derive(…)
at repr(…)
at non_exhaustive
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -358,6 +358,9 @@ fn attr_on_const() {
check(
r#"#[$0] const FOO: () = ();"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -370,9 +373,6 @@ fn attr_on_const() {
at doc(alias = "…")
at must_use
at no_mangle
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -382,6 +382,9 @@ fn attr_on_static() {
check(
r#"#[$0] static FOO: () = ()"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -399,9 +402,6 @@ fn attr_on_static() {
at link_section = "…"
at global_allocator
at used
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -411,6 +411,9 @@ fn attr_on_trait() {
check(
r#"#[$0] trait Foo {}"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -424,9 +427,6 @@ fn attr_on_trait() {
at must_use
at no_mangle
at must_use
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -436,6 +436,9 @@ fn attr_on_impl() {
check(
r#"#[$0] impl () {}"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -449,14 +452,14 @@ fn attr_on_impl() {
at must_use
at no_mangle
at automatically_derived
- kw self
- kw super
- kw crate
"#]],
);
check(
r#"impl () {#![$0]}"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -469,9 +472,6 @@ fn attr_on_impl() {
at doc(alias = "…")
at must_use
at no_mangle
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -481,6 +481,9 @@ fn attr_on_extern_block() {
check(
r#"#[$0] extern {}"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -494,14 +497,14 @@ fn attr_on_extern_block() {
at must_use
at no_mangle
at link
- kw self
- kw super
- kw crate
"#]],
);
check(
r#"extern {#![$0]}"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -515,9 +518,6 @@ fn attr_on_extern_block() {
at must_use
at no_mangle
at link
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -527,6 +527,9 @@ fn attr_on_variant() {
check(
r#"enum Foo { #[$0] Bar }"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -534,9 +537,6 @@ fn attr_on_variant() {
at forbid(…)
at warn(…)
at non_exhaustive
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -546,6 +546,9 @@ fn attr_on_fn() {
check(
r#"#[$0] fn main() {}"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
@@ -573,9 +576,6 @@ fn attr_on_fn() {
at target_feature = "…"
at test
at track_caller
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -586,15 +586,15 @@ fn attr_on_expr() {
check(
r#"fn main() { #[$0] foo() }"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at cfg(…)
at cfg_attr(…)
at deny(…)
at forbid(…)
at warn(…)
- kw self
- kw super
- kw crate
"#]],
);
}
@@ -604,6 +604,9 @@ fn attr_in_source_file_end() {
check(
r#"#[$0]"#,
expect![[r#"
+ kw self::
+ kw super::
+ kw crate::
at allow(…)
at automatically_derived
at cfg(…)
@@ -640,9 +643,6 @@ fn attr_in_source_file_end() {
at track_caller
at used
at warn(…)
- kw self
- kw super
- kw crate
"#]],
);
}
diff --git a/crates/ide_completion/src/tests/fn_param.rs b/crates/ide_completion/src/tests/fn_param.rs
index 662fbe309b..779ec0c3a7 100644
--- a/crates/ide_completion/src/tests/fn_param.rs
+++ b/crates/ide_completion/src/tests/fn_param.rs
@@ -17,6 +17,7 @@ fn baz(file$0) {}
"#,
expect![[r#"
bn file_id: usize
+ kw ref
kw mut
"#]],
);
@@ -32,6 +33,7 @@ fn baz(foo: (), file$0) {}
"#,
expect![[r#"
bn file_id: usize
+ kw ref
kw mut
"#]],
);
@@ -47,6 +49,7 @@ fn baz(file$0 id: u32) {}
"#,
expect![[r#"
bn file_id: usize,
+ kw ref
kw mut
"#]],
);
@@ -60,6 +63,7 @@ fn foo(file_id: usize) {}
fn bar(file_id: u32, $0) {}
"#,
expect![[r#"
+ kw ref
kw mut
"#]],
);
@@ -76,6 +80,7 @@ pub(crate) trait SourceRoot {
"#,
expect![[r#"
bn file_id: usize
+ kw ref
kw mut
"#]],
);
@@ -91,6 +96,7 @@ fn outer(text: &str) {
"#,
expect![[r#"
bn text: &str
+ kw ref
kw mut
"#]],
)
@@ -106,6 +112,7 @@ fn foo2($0) {}
"#,
expect![[r#"
bn Bar { bar }: Bar
+ kw ref
kw mut
bn Bar Bar { bar$1 }: Bar$0
st Bar
@@ -130,6 +137,7 @@ impl A {
bn mut self
bn &mut self
bn file_id: usize
+ kw ref
kw mut
sp Self
st A
@@ -150,6 +158,7 @@ impl A {
"#,
expect![[r#"
bn file_id: usize
+ kw ref
kw mut
sp Self
st A
@@ -178,6 +187,7 @@ fn outer() {
bn foo: i32
bn baz: i32
bn bar: i32
+ kw ref
kw mut
"#]],
)
@@ -202,6 +212,22 @@ fn outer() {
bn baz: i32
bn bar: i32
bn foo: i32
+ kw ref
+ kw mut
+ "#]],
+ )
+}
+
+#[test]
+fn completes_fully_equal() {
+ check(
+ r#"
+fn foo(bar: u32) {}
+fn bar(bar$0) {}
+"#,
+ expect![[r#"
+ bn bar: u32
+ kw ref
kw mut
"#]],
)
diff --git a/crates/ide_completion/src/tests/pattern.rs b/crates/ide_completion/src/tests/pattern.rs
index 7f437e1a07..fe53257672 100644
--- a/crates/ide_completion/src/tests/pattern.rs
+++ b/crates/ide_completion/src/tests/pattern.rs
@@ -22,6 +22,7 @@ fn quux() {
}
"#,
expect![[r#"
+ kw ref
kw mut
"#]],
);
@@ -53,16 +54,13 @@ fn quux() {
#[test]
fn ident_ref_mut_pat() {
- // FIXME mut is already here, don't complete it again
check_empty(
r#"
fn quux() {
let ref mut en$0
}
"#,
- expect![[r#"
- kw mut
- "#]],
+ expect![[r#""#]],
);
check_empty(
r#"
@@ -70,9 +68,7 @@ fn quux() {
let ref mut en$0 @ x
}
"#,
- expect![[r#"
- kw mut
- "#]],
+ expect![[r#""#]],
);
}
@@ -88,16 +84,13 @@ fn quux() {
kw mut
"#]],
);
- // FIXME mut is already here, don't complete it again
check_empty(
r#"
fn quux() {
let &mut en$0
}
"#,
- expect![[r#"
- kw mut
- "#]],
+ expect![[r#""#]],
);
}
@@ -110,6 +103,7 @@ fn foo() {
}
"#,
expect![[r##"
+ kw ref
kw mut
en Enum
bn Record Record { field$1 }$0
@@ -139,6 +133,7 @@ fn foo() {
}
"#,
expect![[r##"
+ kw ref
kw mut
bn Record Record { field$1 }$0
st Record
@@ -160,6 +155,7 @@ fn foo(a$0) {
}
"#,
expect![[r##"
+ kw ref
kw mut
bn Record Record { field$1 }: Record$0
st Record
@@ -175,6 +171,7 @@ fn foo(a$0: Tuple) {
}
"#,
expect![[r##"
+ kw ref
kw mut
bn Record Record { field$1 }$0
st Record
@@ -200,6 +197,7 @@ fn foo() {
}
"#,
expect![[r#"
+ kw ref
kw mut
ma m!(…) macro_rules! m
"#]],
@@ -218,6 +216,7 @@ fn foo() {
}
"#,
expect![[r#"
+ kw ref
kw mut
ev E::X ()
en E
@@ -242,6 +241,7 @@ fn outer() {
}
"#,
expect![[r#"
+ kw ref
kw mut
bn Record Record { field$1, .. }$0
st Record
@@ -267,6 +267,7 @@ impl Foo {
}
"#,
expect![[r#"
+ kw ref
kw mut
bn Self Self($1)$0
sp Self
@@ -278,7 +279,6 @@ impl Foo {
#[test]
fn enum_qualified() {
- // FIXME: Don't show functions, they aren't patterns
check(
r#"
impl Enum {
@@ -291,12 +291,9 @@ fn func() {
}
"#,
expect![[r#"
- ev TupleV(…) (u32)
- ev RecordV {field: u32}
- ev UnitV ()
- ct ASSOC_CONST const ASSOC_CONST: ()
- fn assoc_fn() fn()
- ta AssocType type AssocType = ()
+ ev TupleV(…) (u32)
+ ev RecordV {field: u32}
+ ev UnitV ()
"#]],
);
}
@@ -310,6 +307,7 @@ struct Bar(u32);
fn outer(Foo { bar: $0 }: Foo) {}
"#,
expect![[r#"
+ kw ref
kw mut
bn Foo Foo { bar$1 }$0
st Foo
@@ -340,6 +338,7 @@ struct Bar(u32);
fn foo($0) {}
"#,
expect![[r#"
+ kw ref
kw mut
bn Foo Foo { bar$1 }: Foo$0
st Foo
@@ -360,6 +359,7 @@ fn foo() {
}
"#,
expect![[r#"
+ kw ref
kw mut
bn Foo Foo { bar$1 }$0
st Foo
@@ -368,17 +368,3 @@ fn foo() {
"#]],
)
}
-
-#[test]
-fn completes_fully_equal() {
- check_empty(
- r#"
-fn foo(bar: u32) {}
-fn bar(bar$0) {}
-"#,
- expect![[r#"
- bn bar: u32
- kw mut
- "#]],
- )
-}
diff --git a/crates/ide_completion/src/tests/use_tree.rs b/crates/ide_completion/src/tests/use_tree.rs
index 5f20e342e0..73cb83957f 100644
--- a/crates/ide_completion/src/tests/use_tree.rs
+++ b/crates/ide_completion/src/tests/use_tree.rs
@@ -32,6 +32,25 @@ mod foo {}
}
#[test]
+fn use_tree_start_abs() {
+ cov_mark::check!(use_tree_crate_roots_only);
+ check(
+ r#"
+//- /lib.rs crate:main deps:other_crate
+use ::f$0
+
+struct Foo;
+mod foo {}
+//- /other_crate/lib.rs crate:other_crate
+// nothing here
+"#,
+ expect![[r#"
+ md other_crate
+ "#]],
+ );
+}
+
+#[test]
fn dont_complete_current_use() {
cov_mark::check!(dont_complete_current_use);
check(r#"use self::foo$0;"#, expect![[r#""#]]);
@@ -135,6 +154,25 @@ struct Bar;
}
#[test]
+fn enum_plain_qualified_use_tree() {
+ cov_mark::check!(enum_plain_qualified_use_tree);
+ check(
+ r#"
+use Foo::$0
+
+enum Foo { Variant }
+impl Foo {
+ const CONST: () = ()
+ fn func() {}
+}
+"#,
+ expect![[r#"
+ ev Variant ()
+ "#]],
+ );
+}
+
+#[test]
fn self_qualified_use_tree() {
check(
r#"
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index dcfc2b7bfc..7ea2bc63f2 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -223,7 +223,7 @@ impl Definition {
// def is crate root
// FIXME: We don't do searches for crates currently, as a crate does not actually have a single name
if let &Definition::Module(module) = self {
- if module.crate_root(db) == module {
+ if module.is_crate_root(db) {
return SearchScope::reverse_dependencies(db, module.krate());
}
}
@@ -378,7 +378,7 @@ impl<'a> FindUsages<'a> {
let name = match self.def {
// special case crate modules as these do not have a proper name
- Definition::Module(module) if module.crate_root(self.sema.db) == module => {
+ Definition::Module(module) if module.is_crate_root(self.sema.db) => {
// FIXME: This assumes the crate name is always equal to its display name when it really isn't
module
.krate()
@@ -460,7 +460,7 @@ impl<'a> FindUsages<'a> {
Definition::Module(module) => {
let scope = search_scope.intersection(&SearchScope::module(self.sema.db, module));
- let is_crate_root = module.crate_root(self.sema.db) == module;
+ let is_crate_root = module.is_crate_root(self.sema.db);
for (text, file_id, search_range) in scope_files(sema, &scope) {
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 7211c77e88..067e13ee14 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -119,7 +119,7 @@ impl From<ast::AssocItem> for ast::Item {
}
}
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum AttrKind {
Inner,
Outer,