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 <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2022-02-03 15:01:08 +00:00 committed by GitHub
commit 5a7e11f5fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 700 additions and 356 deletions

View file

@ -481,7 +481,7 @@ impl HirDisplay for Module {
// FIXME: Module doesn't have visibility saved in data. // FIXME: Module doesn't have visibility saved in data.
match self.name(f.db) { match self.name(f.db) {
Some(name) => write!(f, "mod {}", name), 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), Some(name) => write!(f, "extern crate {}", name),
None => write!(f, "extern crate {{unknown}}"), None => write!(f, "extern crate {{unknown}}"),
}, },

View file

@ -452,6 +452,11 @@ impl Module {
Module { id: def_map.module_id(def_map.root()) } 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. /// Iterates over all child modules.
pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> { pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> {
let def_map = self.id.def_map(db.upcast()); let def_map = self.id.def_map(db.upcast());

View file

@ -949,12 +949,15 @@ impl<'db> SemanticsImpl<'db> {
})?; })?;
match res { match res {
Either::Left(path) => resolve_hir_path( Either::Left(path) => {
let len = path.len();
resolve_hir_path(
self.db, self.db,
&self.scope(derive.syntax()).resolver, &self.scope(derive.syntax()).resolver,
&Path::from_known_path(path, []), &Path::from_known_path(path, vec![None; len]),
) )
.filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))), .filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_))))
}
Either::Right(derive) => derive Either::Right(derive) => derive
.map(|call| MacroDef { id: self.db.lookup_intern_macro_call(call).def }) .map(|call| MacroDef { id: self.db.lookup_intern_macro_call(call).def })
.map(PathResolution::Macro), .map(PathResolution::Macro),

View file

@ -92,7 +92,9 @@ impl Path {
path: ModPath, path: ModPath,
generic_args: impl Into<Box<[Option<Interned<GenericArgs>>]>>, generic_args: impl Into<Box<[Option<Interned<GenericArgs>>]>>,
) -> Path { ) -> 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 { pub fn kind(&self) -> &PathKind {

View file

@ -253,6 +253,7 @@ pub enum PointerCast {
/// Go from a mut raw pointer to a const raw pointer. /// Go from a mut raw pointer to a const raw pointer.
MutToConstPointer, MutToConstPointer,
#[allow(dead_code)]
/// Go from `*const [T; N]` to `*const T` /// Go from `*const [T; N]` to `*const T`
ArrayToPointer, ArrayToPointer,

View file

@ -4,6 +4,7 @@ pub(crate) mod attribute;
pub(crate) mod dot; pub(crate) mod dot;
pub(crate) mod flyimport; pub(crate) mod flyimport;
pub(crate) mod fn_param; pub(crate) mod fn_param;
pub(crate) mod format_string;
pub(crate) mod keyword; pub(crate) mod keyword;
pub(crate) mod lifetime; pub(crate) mod lifetime;
pub(crate) mod mod_; pub(crate) mod mod_;
@ -14,7 +15,8 @@ pub(crate) mod record;
pub(crate) mod snippet; pub(crate) mod snippet;
pub(crate) mod trait_impl; pub(crate) mod trait_impl;
pub(crate) mod unqualified_path; pub(crate) mod unqualified_path;
pub(crate) mod format_string; pub(crate) mod use_;
pub(crate) mod vis;
use std::iter; use std::iter;
@ -97,6 +99,19 @@ impl Completions {
item.add_to(self); 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( pub(crate) fn add_resolution(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,

View file

@ -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 //! This module uses a bit of static metadata to provide completions for builtin-in attributes and lints.
//! for built-in attributes.
//! Non-built-in attribute (excluding derives attributes) completions are done in [`super::unqualified_path`].
use ide_db::{ use ide_db::{
helpers::{ helpers::{
@ -16,23 +14,39 @@ use ide_db::{
use itertools::Itertools; use itertools::Itertools;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use rustc_hash::FxHashMap; 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 cfg;
mod derive; mod derive;
mod lint; mod lint;
mod repr; 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 attribute = ctx.fake_attribute_under_caret.as_ref()?;
let name_ref = match attribute.path() { let name_ref = match attribute.path() {
Some(p) => Some(p.as_single_name_ref()?), Some(p) => Some(p.as_single_name_ref()?),
None => None, None => None,
}; };
match (name_ref, attribute.token_tree()) { let (path, tt) = name_ref.zip(attribute.token_tree())?;
(Some(path), Some(tt)) if tt.l_paren_token().is_some() => match path.text().as_str() { if tt.l_paren_token().is_none() {
return None;
}
match path.text().as_str() {
"repr" => repr::complete_repr(acc, ctx, tt), "repr" => repr::complete_repr(acc, ctx, tt),
"derive" => derive::complete_derive(acc, ctx, ctx.attr.as_ref()?), "derive" => derive::complete_derive(acc, ctx, ctx.attr.as_ref()?),
"feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES), "feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES),
@ -42,8 +56,8 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
let lints: Vec<Lint> = CLIPPY_LINT_GROUPS let lints: Vec<Lint> = CLIPPY_LINT_GROUPS
.iter() .iter()
.map(|g| &g.lint) .map(|g| &g.lint)
.chain(DEFAULT_LINTS.iter()) .chain(DEFAULT_LINTS)
.chain(CLIPPY_LINTS.iter()) .chain(CLIPPY_LINTS)
.chain(RUSTDOC_LINTS) .chain(RUSTDOC_LINTS)
.cloned() .cloned()
.collect(); .collect();
@ -54,24 +68,53 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
cfg::complete_cfg(acc, ctx); cfg::complete_cfg(acc, ctx);
} }
_ => (), _ => (),
},
(_, Some(_)) => (),
(_, None) if attribute.expr().is_some() => (),
(_, None) => complete_new_attribute(acc, ctx, attribute),
} }
Some(()) Some(())
} }
// FIXME?: Move this functionality to (un)qualified_path, make this module work solely for builtin/known attributes for their inputs? pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) { let (is_absolute_path, qualifier, is_inner, annotated_item_kind) = match ctx.path_context {
let is_inner = attribute.kind() == ast::AttrKind::Inner; Some(PathCompletionCtx {
let attribute_annotated_item_kind = kind: Some(PathKind::Attr { kind, annotated_item_kind }),
attribute.syntax().parent().map(|it| it.kind()).filter(|_| { is_absolute_path,
is_inner ref qualifier,
// 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() }) => (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);
}
}); });
let attributes = attribute_annotated_item_kind.and_then(|kind| { acc.add_nameref_keywords(ctx);
}
}
let attributes = annotated_item_kind.and_then(|kind| {
if ast::Expr::can_cast(kind) { if ast::Expr::can_cast(kind) {
Some(EXPR_ATTRIBUTES) Some(EXPR_ATTRIBUTES)
} else { } else {

View file

@ -32,7 +32,7 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
if !ctx.config.enable_self_on_the_fly { if !ctx.config.enable_self_on_the_fly {
return; 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; return;
} }
if let Some(func) = ctx.function_def.as_ref().and_then(|fn_| ctx.sema.to_def(fn_)) { if let Some(func) = ctx.function_def.as_ref().and_then(|fn_| ctx.sema.to_def(fn_)) {

View file

@ -171,8 +171,8 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
(PathKind::Type, ItemInNs::Types(_)) => true, (PathKind::Type, ItemInNs::Types(_)) => true,
(PathKind::Type, ItemInNs::Values(_)) => false, (PathKind::Type, ItemInNs::Values(_)) => false,
(PathKind::Attr, ItemInNs::Macros(mac)) => mac.is_attr(), (PathKind::Attr { .. }, ItemInNs::Macros(mac)) => mac.is_attr(),
(PathKind::Attr, _) => false, (PathKind::Attr { .. }, _) => false,
} }
}; };

View file

@ -5,7 +5,7 @@
use syntax::{SyntaxKind, T}; use syntax::{SyntaxKind, T};
use crate::{ use crate::{
context::{PathCompletionContext, PathKind}, context::{PathCompletionCtx, PathKind},
patterns::ImmediateLocation, patterns::ImmediateLocation,
CompletionContext, CompletionItem, CompletionItemKind, Completions, 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); cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
return; return;
} }
if ctx.pattern_ctx.is_some() {
return;
}
let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet); 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 has_block_expr_parent = ctx.has_block_expr_parent();
let expects_item = ctx.expects_item(); let expects_item = ctx.expects_item();
if let Some(PathKind::Vis { has_in_token }) = ctx.path_kind() { if let Some(PathKind::Vis { .. }) = ctx.path_kind() {
if !has_in_token {
cov_mark::hit!(kw_completion_in);
add_keyword("in", "in");
}
return; return;
} }
if ctx.has_impl_or_trait_prev_sibling() { 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}"); 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 "); add_keyword("mut", "mut ");
} }
let (can_be_stmt, in_loop_body) = match ctx.path_context { let (can_be_stmt, in_loop_body) = match ctx.path_context {
Some(PathCompletionContext { Some(PathCompletionCtx { is_absolute_path: false, can_be_stmt, in_loop_body, .. }) => {
is_trivial_path: true, can_be_stmt, in_loop_body, .. (can_be_stmt, in_loop_body)
}) => (can_be_stmt, in_loop_body), }
_ => return, _ => return,
}; };

View file

@ -1,20 +1,52 @@
//! Completes constants and paths in unqualified patterns. //! 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::{ use crate::{
context::{PatternContext, PatternRefutability}, context::{PathCompletionCtx, PathQualifierCtx, PatternRefutability},
CompletionContext, Completions, CompletionContext, Completions,
}; };
/// Completes constants and paths in unqualified patterns. /// Completes constants and paths in unqualified patterns.
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
let refutable = match ctx.pattern_ctx { let patctx = match &ctx.pattern_ctx {
Some(PatternContext { refutability, .. }) if ctx.path_context.is_none() => { Some(ctx) => ctx,
refutability == PatternRefutability::Refutable
}
_ => return, _ => 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; let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
if let Some(hir::Adt::Enum(e)) = 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);
}
}
}

View file

@ -1,14 +1,12 @@
//! Completion of paths, i.e. `some::prefix::$0`. //! Completion of paths, i.e. `some::prefix::$0`.
use std::iter;
use hir::{ScopeDef, Trait}; use hir::{ScopeDef, Trait};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use syntax::{ast, AstNode}; use syntax::ast;
use crate::{ use crate::{
completions::{module_or_attr, module_or_fn_macro}, completions::module_or_fn_macro,
context::{PathCompletionContext, PathKind}, context::{PathCompletionCtx, PathKind},
patterns::ImmediateLocation, patterns::ImmediateLocation,
CompletionContext, Completions, 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() { if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
return; 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 // let ... else, syntax would come in really handy here right now
Some(PathCompletionContext { Some(PathCompletionCtx { qualifier: Some(ref qualifier), kind, .. }) => (qualifier, kind),
qualifier: Some(ref qualifier),
use_tree_parent,
kind,
..
}) => (qualifier, use_tree_parent, kind),
_ => return, _ => return,
}; };
// special case `<_>::$0` as this doesn't resolve to anything. // special case `<_>::$0` as this doesn't resolve to anything.
if path.qualifier().is_none() { if qualifier.path.qualifier().is_none() {
if matches!( if matches!(
path.segment().and_then(|it| it.kind()), qualifier.path.segment().and_then(|it| it.kind()),
Some(ast::PathSegmentKind::Type { Some(ast::PathSegmentKind::Type {
type_ref: Some(ast::Type::InferType(_)), type_ref: Some(ast::Type::InferType(_)),
trait_ref: None, 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, Some(res) => res,
None => return, None => return,
}; };
let context_module = ctx.module;
match ctx.completion_location { match ctx.completion_location {
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => { Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { 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) { if let Some(def) = module_or_fn_macro(def) {
acc.add_resolution(ctx, name, def); acc.add_resolution(ctx, name, def);
} }
@ -69,78 +63,22 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
} }
match kind { match kind {
// Complete next child module that comes after the qualified module which is still our parent Some(PathKind::Pat | PathKind::Attr { .. } | PathKind::Vis { .. } | PathKind::Use) => {
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; 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);
}
}
}
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");
}
}
_ => (),
}
if !matches!(kind, Some(PathKind::Pat)) {
// Add associated types on type parameters and `Self`. // Add associated types on type parameters and `Self`.
ctx.scope.assoc_type_shorthand_candidates(&resolution, |_, alias| { ctx.scope.assoc_type_shorthand_candidates(&resolution, |_, alias| {
acc.add_type_alias(ctx, alias); acc.add_type_alias(ctx, alias);
None::<()> None::<()>
}); });
} }
}
match resolution { match resolution {
hir::PathResolution::Def(hir::ModuleDef::Module(module)) => { 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 { 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 { let add_resolution = match def {
// Don't suggest attribute macros and derives. // Don't suggest attribute macros and derives.
ScopeDef::MacroDef(mac) => mac.is_fn_like(), 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::TypeAlias(_)
| hir::ModuleDef::BuiltinType(_)), | 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); add_enum_variants(acc, ctx, e);
} }
let ty = match def { let ty = match def {
@ -622,18 +560,6 @@ fn foo() {
); );
} }
#[test]
fn dont_complete_attr() {
check(
r#"
mod foo { pub struct Foo; }
#[foo::$0]
fn f() {}
"#,
expect![[""]],
);
}
#[test] #[test]
fn completes_variant_through_self() { fn completes_variant_through_self() {
check( check(

View file

@ -5,7 +5,7 @@ use ide_db::helpers::{insert_use::ImportScope, SnippetCap};
use syntax::T; use syntax::T;
use crate::{ use crate::{
context::PathCompletionContext, item::Builder, CompletionContext, CompletionItem, context::PathCompletionCtx, item::Builder, CompletionContext, CompletionItem,
CompletionItemKind, Completions, SnippetScope, 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 { 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, _ => return,
}; };

View file

@ -4,8 +4,8 @@ use hir::ScopeDef;
use syntax::{ast, AstNode}; use syntax::{ast, AstNode};
use crate::{ use crate::{
completions::{module_or_attr, module_or_fn_macro}, completions::module_or_fn_macro,
context::{PathCompletionContext, PathKind}, context::{PathCompletionCtx, PathKind},
patterns::ImmediateLocation, patterns::ImmediateLocation,
CompletionContext, Completions, CompletionContext, Completions,
}; };
@ -15,38 +15,23 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() { if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
return; return;
} }
let kind = match ctx.path_context { match ctx.path_context {
Some(PathCompletionContext { is_trivial_path: true, kind, .. }) => kind, Some(PathCompletionCtx {
kind:
Some(
PathKind::Vis { .. }
| PathKind::Attr { .. }
| PathKind::Use { .. }
| PathKind::Pat,
),
..
}) => return,
Some(PathCompletionCtx { is_absolute_path: false, qualifier: None, .. }) => (),
_ => return, _ => 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)); ["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;
}
_ => (),
}
match &ctx.completion_location { match &ctx.completion_location {
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => { Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
// only show macros in {Assoc}ItemList // only show macros in {Assoc}ItemList

View file

@ -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);
}
}
}

View file

@ -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));
}
_ => {}
}
}

View file

@ -3,7 +3,9 @@
use std::iter; use std::iter;
use base_db::SourceDatabaseExt; 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::{ use ide_db::{
active_parameter::ActiveParameter, active_parameter::ActiveParameter,
base_db::{FilePosition, SourceDatabase}, base_db::{FilePosition, SourceDatabase},
@ -11,8 +13,8 @@ use ide_db::{
RootDatabase, RootDatabase,
}; };
use syntax::{ use syntax::{
algo::find_node_at_offset, algo::{find_node_at_offset, non_trivia_sibling},
ast::{self, HasName, NameOrNameRef}, ast::{self, AttrKind, HasName, NameOrNameRef},
match_ast, AstNode, NodeOrToken, match_ast, AstNode, NodeOrToken,
SyntaxKind::{self, *}, SyntaxKind::{self, *},
SyntaxNode, SyntaxToken, TextRange, TextSize, T, SyntaxNode, SyntaxToken, TextRange, TextSize, T,
@ -34,6 +36,7 @@ pub(crate) enum PatternRefutability {
Refutable, Refutable,
Irrefutable, Irrefutable,
} }
pub(crate) enum Visible { pub(crate) enum Visible {
Yes, Yes,
Editable, Editable,
@ -44,7 +47,7 @@ pub(crate) enum Visible {
pub(super) enum PathKind { pub(super) enum PathKind {
Expr, Expr,
Type, Type,
Attr, Attr { kind: AttrKind, annotated_item_kind: Option<SyntaxKind> },
Mac, Mac,
Pat, Pat,
Vis { has_in_token: bool }, Vis { has_in_token: bool },
@ -52,18 +55,13 @@ pub(super) enum PathKind {
} }
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct PathCompletionContext { pub(crate) struct PathCompletionCtx {
/// If this is a call with () already there /// If this is a call with () already there
has_call_parens: bool, has_call_parens: bool,
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. /// Whether this path stars with a `::`.
pub(super) is_trivial_path: bool, pub(super) is_absolute_path: bool,
/// If not a trivial path, the prefix (qualifier). /// The qualifier of the current path if it exists.
pub(super) qualifier: Option<ast::Path>, pub(super) qualifier: Option<PathQualifierCtx>,
#[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,
pub(super) kind: Option<PathKind>, pub(super) kind: Option<PathKind>,
/// Whether the path segment has type args or not. /// Whether the path segment has type args or not.
pub(super) has_type_args: bool, pub(super) has_type_args: bool,
@ -72,11 +70,24 @@ pub(crate) struct PathCompletionContext {
pub(super) in_loop_body: bool, pub(super) in_loop_body: bool,
} }
#[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)] #[derive(Debug)]
pub(super) struct PatternContext { pub(super) struct PatternContext {
pub(super) refutability: PatternRefutability, pub(super) refutability: PatternRefutability,
pub(super) param_ctx: Option<(ast::ParamList, ast::Param, ParamKind)>, pub(super) param_ctx: Option<(ast::ParamList, ast::Param, ParamKind)>,
pub(super) has_type_ascription: bool, 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)] #[derive(Debug)]
@ -129,7 +140,7 @@ pub(crate) struct CompletionContext<'a> {
pub(super) lifetime_ctx: Option<LifetimeContext>, pub(super) lifetime_ctx: Option<LifetimeContext>,
pub(super) pattern_ctx: Option<PatternContext>, pub(super) pattern_ctx: Option<PatternContext>,
pub(super) path_context: Option<PathCompletionContext>, pub(super) path_context: Option<PathCompletionCtx>,
pub(super) locals: Vec<(Name, Local)>, pub(super) locals: Vec<(Name, Local)>,
@ -211,11 +222,8 @@ impl<'a> CompletionContext<'a> {
matches!(self.completion_location, Some(ImmediateLocation::StmtList)) matches!(self.completion_location, Some(ImmediateLocation::StmtList))
} }
pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool { pub(crate) fn expects_ident_ref_expr(&self) -> bool {
matches!( matches!(self.completion_location, Some(ImmediateLocation::RefExpr))
self.completion_location,
Some(ImmediateLocation::IdentPat | ImmediateLocation::RefExpr)
)
} }
pub(crate) fn expect_field(&self) -> bool { pub(crate) fn expect_field(&self) -> bool {
@ -262,27 +270,29 @@ impl<'a> CompletionContext<'a> {
} }
pub(crate) fn expects_expression(&self) -> bool { 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 { 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 { pub(crate) fn path_is_call(&self) -> bool {
self.path_context.as_ref().map_or(false, |it| it.has_call_parens) 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 { 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> { 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> { pub(crate) fn path_kind(&self) -> Option<PathKind> {
@ -779,37 +789,33 @@ impl<'a> CompletionContext<'a> {
if is_name_in_field_pat { if is_name_in_field_pat {
return None; return None;
} }
if !bind_pat.is_simple_ident() {
return None;
}
Some(pattern_context_for(original_file, bind_pat.into())) Some(pattern_context_for(original_file, bind_pat.into()))
} }
fn classify_name_ref( fn classify_name_ref(
_sema: &Semantics<RootDatabase>, sema: &Semantics<RootDatabase>,
original_file: &SyntaxNode, original_file: &SyntaxNode,
name_ref: ast::NameRef, name_ref: ast::NameRef,
) -> Option<(PathCompletionContext, Option<PatternContext>)> { ) -> Option<(PathCompletionCtx, Option<PatternContext>)> {
let parent = name_ref.syntax().parent()?; let parent = name_ref.syntax().parent()?;
let segment = ast::PathSegment::cast(parent)?; let segment = ast::PathSegment::cast(parent)?;
let path = segment.parent_path(); let path = segment.parent_path();
let mut path_ctx = PathCompletionContext { let mut path_ctx = PathCompletionCtx {
has_call_parens: false, has_call_parens: false,
is_trivial_path: false, is_absolute_path: false,
qualifier: None, qualifier: None,
parent: None,
has_type_args: false, has_type_args: false,
can_be_stmt: false, can_be_stmt: false,
in_loop_body: false, in_loop_body: false,
use_tree_parent: false,
kind: None, kind: None,
}; };
let mut pat_ctx = None; let mut pat_ctx = None;
path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax()); path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax());
path_ctx.kind = path.syntax().ancestors().find_map(|it| { path_ctx.kind = path.syntax().ancestors().find_map(|it| {
match_ast! { // using Option<Option<PathKind>> as extra controlflow
let kind = match_ast! {
match it { match it {
ast::PathType(_) => Some(PathKind::Type), ast::PathType(_) => Some(PathKind::Type),
ast::PathExpr(it) => { ast::PathExpr(it) => {
@ -830,32 +836,57 @@ impl<'a> CompletionContext<'a> {
Some(PathKind::Pat) Some(PathKind::Pat)
}, },
ast::MacroCall(it) => it.excl_token().and(Some(PathKind::Mac)), 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::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
ast::UseTree(_) => Some(PathKind::Use), ast::UseTree(_) => Some(PathKind::Use),
_ => None, _ => return None,
} }
} };
}); Some(kind)
}).flatten();
path_ctx.has_type_args = segment.generic_arg_list().is_some(); path_ctx.has_type_args = segment.generic_arg_list().is_some();
if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) { if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
path_ctx.use_tree_parent = use_tree_parent; if !use_tree_parent {
path_ctx.qualifier = path path_ctx.is_absolute_path =
path.top_path().segment().map_or(false, |it| it.coloncolon_token().is_some());
}
let path = path
.segment() .segment()
.and_then(|it| find_node_in_file(original_file, &it)) .and_then(|it| find_node_in_file(original_file, &it))
.map(|it| it.parent_path()); .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)); return Some((path_ctx, pat_ctx));
} }
if let Some(segment) = path.segment() { if let Some(segment) = path.segment() {
if segment.coloncolon_token().is_some() { if segment.coloncolon_token().is_some() {
path_ctx.is_absolute_path = true;
return Some((path_ctx, pat_ctx)); return Some((path_ctx, pat_ctx));
} }
} }
path_ctx.is_trivial_path = true;
// Find either enclosing expr statement (thing with `;`) or a // Find either enclosing expr statement (thing with `;`) or a
// block. If block, check that we are the last expr. // block. If block, check that we are the last expr.
path_ctx.can_be_stmt = name_ref path_ctx.can_be_stmt = name_ref
@ -915,7 +946,18 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont
}; };
(refutability, false) (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> { 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_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::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 { fn has_ref(token: &SyntaxToken) -> bool {

View file

@ -151,7 +151,10 @@ pub fn completions(
} }
let mut acc = Completions::default(); let mut acc = Completions::default();
completions::attribute::complete_known_attribute_input(&mut acc, &ctx);
completions::attribute::complete_attribute(&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::fn_param::complete_fn_param(&mut acc, &ctx);
completions::keyword::complete_expr_keyword(&mut acc, &ctx); completions::keyword::complete_expr_keyword(&mut acc, &ctx);
completions::snippet::complete_expr_snippet(&mut acc, &ctx); completions::snippet::complete_expr_snippet(&mut acc, &ctx);

View file

@ -19,7 +19,7 @@ use ide_db::{
use syntax::{SmolStr, SyntaxKind, TextRange}; use syntax::{SmolStr, SyntaxKind, TextRange};
use crate::{ use crate::{
context::{PathCompletionContext, PathKind}, context::{PathCompletionCtx, PathKind},
item::{CompletionRelevanceTypeMatch, ImportEdit}, item::{CompletionRelevanceTypeMatch, ImportEdit},
render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}, render::{enum_variant::render_variant, function::render_fn, macro_::render_macro},
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
@ -234,7 +234,7 @@ fn render_resolution_(
// Add `<>` for generic types // Add `<>` for generic types
let type_path_no_ty_args = matches!( let type_path_no_ty_args = matches!(
ctx.completion.path_context, 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; ) && ctx.completion.config.add_call_parenthesis;
if type_path_no_ty_args { if type_path_no_ty_args {
if let Some(cap) = ctx.snippet_cap() { if let Some(cap) = ctx.snippet_cap() {

View file

@ -17,6 +17,10 @@ fn proc_macros() {
struct Foo; struct Foo;
"#, "#,
expect![[r#" expect![[r#"
md proc_macros
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -32,10 +36,6 @@ struct Foo;
at derive() at derive()
at repr() at repr()
at non_exhaustive at non_exhaustive
kw self
kw super
kw crate
md proc_macros
"#]], "#]],
) )
} }
@ -78,15 +78,15 @@ fn with_existing_attr() {
check( check(
r#"#[no_mangle] #[$0] mcall!();"#, r#"#[no_mangle] #[$0] mcall!();"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
at deny() at deny()
at forbid() at forbid()
at warn() at warn()
kw self
kw super
kw crate
"#]], "#]],
) )
} }
@ -96,6 +96,9 @@ fn attr_on_source_file() {
check( check(
r#"#![$0]"#, r#"#![$0]"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -116,9 +119,6 @@ fn attr_on_source_file() {
at recursion_limit = "" at recursion_limit = ""
at type_length_limit = at type_length_limit =
at windows_subsystem = "" at windows_subsystem = ""
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -128,6 +128,9 @@ fn attr_on_module() {
check( check(
r#"#[$0] mod foo;"#, r#"#[$0] mod foo;"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -142,14 +145,14 @@ fn attr_on_module() {
at no_mangle at no_mangle
at macro_use at macro_use
at path = "" at path = ""
kw self
kw super
kw crate
"#]], "#]],
); );
check( check(
r#"mod foo {#![$0]}"#, r#"mod foo {#![$0]}"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -163,9 +166,6 @@ fn attr_on_module() {
at must_use at must_use
at no_mangle at no_mangle
at no_implicit_prelude at no_implicit_prelude
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -175,6 +175,9 @@ fn attr_on_macro_rules() {
check( check(
r#"#[$0] macro_rules! foo {}"#, r#"#[$0] macro_rules! foo {}"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -189,9 +192,6 @@ fn attr_on_macro_rules() {
at no_mangle at no_mangle
at macro_export at macro_export
at macro_use at macro_use
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -201,6 +201,9 @@ fn attr_on_macro_def() {
check( check(
r#"#[$0] macro foo {}"#, r#"#[$0] macro foo {}"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -213,9 +216,6 @@ fn attr_on_macro_def() {
at doc(alias = "") at doc(alias = "")
at must_use at must_use
at no_mangle at no_mangle
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -225,6 +225,9 @@ fn attr_on_extern_crate() {
check( check(
r#"#[$0] extern crate foo;"#, r#"#[$0] extern crate foo;"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -238,9 +241,6 @@ fn attr_on_extern_crate() {
at must_use at must_use
at no_mangle at no_mangle
at macro_use at macro_use
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -250,6 +250,9 @@ fn attr_on_use() {
check( check(
r#"#[$0] use foo;"#, r#"#[$0] use foo;"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -262,9 +265,6 @@ fn attr_on_use() {
at doc(alias = "") at doc(alias = "")
at must_use at must_use
at no_mangle at no_mangle
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -274,6 +274,9 @@ fn attr_on_type_alias() {
check( check(
r#"#[$0] type foo = ();"#, r#"#[$0] type foo = ();"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -286,9 +289,6 @@ fn attr_on_type_alias() {
at doc(alias = "") at doc(alias = "")
at must_use at must_use
at no_mangle at no_mangle
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -302,6 +302,11 @@ fn attr_on_struct() {
struct Foo; struct Foo;
"#, "#,
expect![[r#" expect![[r#"
md core
at derive pub macro derive
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -317,11 +322,6 @@ struct Foo;
at derive() at derive()
at repr() at repr()
at non_exhaustive 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( check(
r#"#[$0] enum Foo {}"#, r#"#[$0] enum Foo {}"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -346,9 +349,6 @@ fn attr_on_enum() {
at derive() at derive()
at repr() at repr()
at non_exhaustive at non_exhaustive
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -358,6 +358,9 @@ fn attr_on_const() {
check( check(
r#"#[$0] const FOO: () = ();"#, r#"#[$0] const FOO: () = ();"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -370,9 +373,6 @@ fn attr_on_const() {
at doc(alias = "") at doc(alias = "")
at must_use at must_use
at no_mangle at no_mangle
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -382,6 +382,9 @@ fn attr_on_static() {
check( check(
r#"#[$0] static FOO: () = ()"#, r#"#[$0] static FOO: () = ()"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -399,9 +402,6 @@ fn attr_on_static() {
at link_section = "" at link_section = ""
at global_allocator at global_allocator
at used at used
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -411,6 +411,9 @@ fn attr_on_trait() {
check( check(
r#"#[$0] trait Foo {}"#, r#"#[$0] trait Foo {}"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -424,9 +427,6 @@ fn attr_on_trait() {
at must_use at must_use
at no_mangle at no_mangle
at must_use at must_use
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -436,6 +436,9 @@ fn attr_on_impl() {
check( check(
r#"#[$0] impl () {}"#, r#"#[$0] impl () {}"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -449,14 +452,14 @@ fn attr_on_impl() {
at must_use at must_use
at no_mangle at no_mangle
at automatically_derived at automatically_derived
kw self
kw super
kw crate
"#]], "#]],
); );
check( check(
r#"impl () {#![$0]}"#, r#"impl () {#![$0]}"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -469,9 +472,6 @@ fn attr_on_impl() {
at doc(alias = "") at doc(alias = "")
at must_use at must_use
at no_mangle at no_mangle
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -481,6 +481,9 @@ fn attr_on_extern_block() {
check( check(
r#"#[$0] extern {}"#, r#"#[$0] extern {}"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -494,14 +497,14 @@ fn attr_on_extern_block() {
at must_use at must_use
at no_mangle at no_mangle
at link at link
kw self
kw super
kw crate
"#]], "#]],
); );
check( check(
r#"extern {#![$0]}"#, r#"extern {#![$0]}"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -515,9 +518,6 @@ fn attr_on_extern_block() {
at must_use at must_use
at no_mangle at no_mangle
at link at link
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -527,6 +527,9 @@ fn attr_on_variant() {
check( check(
r#"enum Foo { #[$0] Bar }"#, r#"enum Foo { #[$0] Bar }"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -534,9 +537,6 @@ fn attr_on_variant() {
at forbid() at forbid()
at warn() at warn()
at non_exhaustive at non_exhaustive
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -546,6 +546,9 @@ fn attr_on_fn() {
check( check(
r#"#[$0] fn main() {}"#, r#"#[$0] fn main() {}"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
@ -573,9 +576,6 @@ fn attr_on_fn() {
at target_feature = "" at target_feature = ""
at test at test
at track_caller at track_caller
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -586,15 +586,15 @@ fn attr_on_expr() {
check( check(
r#"fn main() { #[$0] foo() }"#, r#"fn main() { #[$0] foo() }"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at cfg() at cfg()
at cfg_attr() at cfg_attr()
at deny() at deny()
at forbid() at forbid()
at warn() at warn()
kw self
kw super
kw crate
"#]], "#]],
); );
} }
@ -604,6 +604,9 @@ fn attr_in_source_file_end() {
check( check(
r#"#[$0]"#, r#"#[$0]"#,
expect![[r#" expect![[r#"
kw self::
kw super::
kw crate::
at allow() at allow()
at automatically_derived at automatically_derived
at cfg() at cfg()
@ -640,9 +643,6 @@ fn attr_in_source_file_end() {
at track_caller at track_caller
at used at used
at warn() at warn()
kw self
kw super
kw crate
"#]], "#]],
); );
} }

View file

@ -17,6 +17,7 @@ fn baz(file$0) {}
"#, "#,
expect![[r#" expect![[r#"
bn file_id: usize bn file_id: usize
kw ref
kw mut kw mut
"#]], "#]],
); );
@ -32,6 +33,7 @@ fn baz(foo: (), file$0) {}
"#, "#,
expect![[r#" expect![[r#"
bn file_id: usize bn file_id: usize
kw ref
kw mut kw mut
"#]], "#]],
); );
@ -47,6 +49,7 @@ fn baz(file$0 id: u32) {}
"#, "#,
expect![[r#" expect![[r#"
bn file_id: usize, bn file_id: usize,
kw ref
kw mut kw mut
"#]], "#]],
); );
@ -60,6 +63,7 @@ fn foo(file_id: usize) {}
fn bar(file_id: u32, $0) {} fn bar(file_id: u32, $0) {}
"#, "#,
expect![[r#" expect![[r#"
kw ref
kw mut kw mut
"#]], "#]],
); );
@ -76,6 +80,7 @@ pub(crate) trait SourceRoot {
"#, "#,
expect![[r#" expect![[r#"
bn file_id: usize bn file_id: usize
kw ref
kw mut kw mut
"#]], "#]],
); );
@ -91,6 +96,7 @@ fn outer(text: &str) {
"#, "#,
expect![[r#" expect![[r#"
bn text: &str bn text: &str
kw ref
kw mut kw mut
"#]], "#]],
) )
@ -106,6 +112,7 @@ fn foo2($0) {}
"#, "#,
expect![[r#" expect![[r#"
bn Bar { bar }: Bar bn Bar { bar }: Bar
kw ref
kw mut kw mut
bn Bar Bar { bar$1 }: Bar$0 bn Bar Bar { bar$1 }: Bar$0
st Bar st Bar
@ -130,6 +137,7 @@ impl A {
bn mut self bn mut self
bn &mut self bn &mut self
bn file_id: usize bn file_id: usize
kw ref
kw mut kw mut
sp Self sp Self
st A st A
@ -150,6 +158,7 @@ impl A {
"#, "#,
expect![[r#" expect![[r#"
bn file_id: usize bn file_id: usize
kw ref
kw mut kw mut
sp Self sp Self
st A st A
@ -178,6 +187,7 @@ fn outer() {
bn foo: i32 bn foo: i32
bn baz: i32 bn baz: i32
bn bar: i32 bn bar: i32
kw ref
kw mut kw mut
"#]], "#]],
) )
@ -202,6 +212,22 @@ fn outer() {
bn baz: i32 bn baz: i32
bn bar: i32 bn bar: i32
bn foo: 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 kw mut
"#]], "#]],
) )

View file

@ -22,6 +22,7 @@ fn quux() {
} }
"#, "#,
expect![[r#" expect![[r#"
kw ref
kw mut kw mut
"#]], "#]],
); );
@ -53,16 +54,13 @@ fn quux() {
#[test] #[test]
fn ident_ref_mut_pat() { fn ident_ref_mut_pat() {
// FIXME mut is already here, don't complete it again
check_empty( check_empty(
r#" r#"
fn quux() { fn quux() {
let ref mut en$0 let ref mut en$0
} }
"#, "#,
expect![[r#" expect![[r#""#]],
kw mut
"#]],
); );
check_empty( check_empty(
r#" r#"
@ -70,9 +68,7 @@ fn quux() {
let ref mut en$0 @ x let ref mut en$0 @ x
} }
"#, "#,
expect![[r#" expect![[r#""#]],
kw mut
"#]],
); );
} }
@ -88,16 +84,13 @@ fn quux() {
kw mut kw mut
"#]], "#]],
); );
// FIXME mut is already here, don't complete it again
check_empty( check_empty(
r#" r#"
fn quux() { fn quux() {
let &mut en$0 let &mut en$0
} }
"#, "#,
expect![[r#" expect![[r#""#]],
kw mut
"#]],
); );
} }
@ -110,6 +103,7 @@ fn foo() {
} }
"#, "#,
expect![[r##" expect![[r##"
kw ref
kw mut kw mut
en Enum en Enum
bn Record Record { field$1 }$0 bn Record Record { field$1 }$0
@ -139,6 +133,7 @@ fn foo() {
} }
"#, "#,
expect![[r##" expect![[r##"
kw ref
kw mut kw mut
bn Record Record { field$1 }$0 bn Record Record { field$1 }$0
st Record st Record
@ -160,6 +155,7 @@ fn foo(a$0) {
} }
"#, "#,
expect![[r##" expect![[r##"
kw ref
kw mut kw mut
bn Record Record { field$1 }: Record$0 bn Record Record { field$1 }: Record$0
st Record st Record
@ -175,6 +171,7 @@ fn foo(a$0: Tuple) {
} }
"#, "#,
expect![[r##" expect![[r##"
kw ref
kw mut kw mut
bn Record Record { field$1 }$0 bn Record Record { field$1 }$0
st Record st Record
@ -200,6 +197,7 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
kw ref
kw mut kw mut
ma m!() macro_rules! m ma m!() macro_rules! m
"#]], "#]],
@ -218,6 +216,7 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
kw ref
kw mut kw mut
ev E::X () ev E::X ()
en E en E
@ -242,6 +241,7 @@ fn outer() {
} }
"#, "#,
expect![[r#" expect![[r#"
kw ref
kw mut kw mut
bn Record Record { field$1, .. }$0 bn Record Record { field$1, .. }$0
st Record st Record
@ -267,6 +267,7 @@ impl Foo {
} }
"#, "#,
expect![[r#" expect![[r#"
kw ref
kw mut kw mut
bn Self Self($1)$0 bn Self Self($1)$0
sp Self sp Self
@ -278,7 +279,6 @@ impl Foo {
#[test] #[test]
fn enum_qualified() { fn enum_qualified() {
// FIXME: Don't show functions, they aren't patterns
check( check(
r#" r#"
impl Enum { impl Enum {
@ -294,9 +294,6 @@ fn func() {
ev TupleV() (u32) ev TupleV() (u32)
ev RecordV {field: u32} ev RecordV {field: u32}
ev UnitV () ev UnitV ()
ct ASSOC_CONST const ASSOC_CONST: ()
fn assoc_fn() fn()
ta AssocType type AssocType = ()
"#]], "#]],
); );
} }
@ -310,6 +307,7 @@ struct Bar(u32);
fn outer(Foo { bar: $0 }: Foo) {} fn outer(Foo { bar: $0 }: Foo) {}
"#, "#,
expect![[r#" expect![[r#"
kw ref
kw mut kw mut
bn Foo Foo { bar$1 }$0 bn Foo Foo { bar$1 }$0
st Foo st Foo
@ -340,6 +338,7 @@ struct Bar(u32);
fn foo($0) {} fn foo($0) {}
"#, "#,
expect![[r#" expect![[r#"
kw ref
kw mut kw mut
bn Foo Foo { bar$1 }: Foo$0 bn Foo Foo { bar$1 }: Foo$0
st Foo st Foo
@ -360,6 +359,7 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
kw ref
kw mut kw mut
bn Foo Foo { bar$1 }$0 bn Foo Foo { bar$1 }$0
st Foo 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
"#]],
)
}

View file

@ -31,6 +31,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] #[test]
fn dont_complete_current_use() { fn dont_complete_current_use() {
cov_mark::check!(dont_complete_current_use); cov_mark::check!(dont_complete_current_use);
@ -134,6 +153,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] #[test]
fn self_qualified_use_tree() { fn self_qualified_use_tree() {
check( check(

View file

@ -223,7 +223,7 @@ impl Definition {
// def is crate root // def is crate root
// FIXME: We don't do searches for crates currently, as a crate does not actually have a single name // 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 let &Definition::Module(module) = self {
if module.crate_root(db) == module { if module.is_crate_root(db) {
return SearchScope::reverse_dependencies(db, module.krate()); return SearchScope::reverse_dependencies(db, module.krate());
} }
} }
@ -378,7 +378,7 @@ impl<'a> FindUsages<'a> {
let name = match self.def { let name = match self.def {
// special case crate modules as these do not have a proper name // 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 // FIXME: This assumes the crate name is always equal to its display name when it really isn't
module module
.krate() .krate()
@ -460,7 +460,7 @@ impl<'a> FindUsages<'a> {
Definition::Module(module) => { Definition::Module(module) => {
let scope = search_scope.intersection(&SearchScope::module(self.sema.db, 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) { for (text, file_id, search_range) in scope_files(sema, &scope) {
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone()); let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());

View file

@ -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 { pub enum AttrKind {
Inner, Inner,
Outer, Outer,