mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 09:27:27 +00:00
Merge #11397
11397: internal: Refactor completion module split r=Veykril a=Veykril Currently our completion infra is split into several modules, each trying to do completions for something specific. This "something" is rather unstructured as it stands now, we have a module for `flyimporting path` completions, `unqualified` and `qualified path` completions, modules for `pattern position` completions that only try to complete extra things for patterns that aren't done in the path modules, `attribute` completions that again only try to add builtin attribute completions without adding the normal path completions and a bunch of other special "entity" completions like lifetimes, method call/field access, function param cloning, ... which serve a more specific purpose than the previous listed ones. As is evident, the former mentioned ones have some decent overlap which requires extra filtering in them so that they don't collide with each other duplicating a bunch of completions(which we had happen in the past at times). Now this overlap mostly happens with path completions(and keyword completions as well in some sense) which gives me the feeling that having `qualified` and `unqualified` path completions be separate from the rest gives us more troubles than benefits in the long run. So this is an attempt at changing this structure to instead still go by rough entity for special cases, but when it comes to paths we instead do the module split on the "path kinds"/"locations"(think pattern, type, expr position etc) that exist. This goes hand in hand with the test refactoring I have done that moved tests to "location oriented" modules as well as the `CompletionContext` refactoring that actually already started splitting the context up for path kinds. This PR moves some path completions out of the `qualified` and `unqualified` path modules namely attribute, visibility, use and pattern paths. Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
5a7e11f5fb
25 changed files with 700 additions and 356 deletions
|
@ -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}}"),
|
||||
},
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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_)) {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(PathKind::Pat | PathKind::Attr { .. } | PathKind::Vis { .. } | PathKind::Use) => {
|
||||
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;
|
||||
_ => {
|
||||
// Add associated types on type parameters and `Self`.
|
||||
ctx.scope.assoc_type_shorthand_candidates(&resolution, |_, alias| {
|
||||
acc.add_type_alias(ctx, alias);
|
||||
None::<()>
|
||||
});
|
||||
}
|
||||
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`.
|
||||
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 {
|
||||
|
@ -622,18 +560,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(
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,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() {
|
||||
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;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match &ctx.completion_location {
|
||||
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
|
||||
// only show macros in {Assoc}ItemList
|
||||
|
|
95
crates/ide_completion/src/completions/use_.rs
Normal file
95
crates/ide_completion/src/completions/use_.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
52
crates/ide_completion/src/completions/vis.rs
Normal file
52
crates/ide_completion/src/completions/vis.rs
Normal 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));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
@ -72,11 +70,24 @@ pub(crate) struct PathCompletionContext {
|
|||
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)]
|
||||
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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
"#]],
|
||||
)
|
||||
|
|
|
@ -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
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
fn 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]
|
||||
fn self_qualified_use_tree() {
|
||||
check(
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue