Unnamed repository; edit this file 'description' to name the repository.
-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,