mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +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.
|
// 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}}"),
|
||||||
},
|
},
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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_)) {
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
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 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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
|
||||||
"#]],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue