10943: feat: Enable completions for attributes r=Veykril a=Veykril



Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-12-05 15:14:10 +00:00 committed by GitHub
commit 6f84bbfa1e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 217 additions and 34 deletions

View file

@ -1716,6 +1716,10 @@ impl MacroDef {
MacroKind::Attr | MacroKind::Derive => false,
}
}
pub fn is_attr(&self) -> bool {
matches!(self.kind(), MacroKind::Attr)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]

View file

@ -1,13 +1,14 @@
//! See [`import_on_the_fly`].
use hir::ItemInNs;
use ide_db::helpers::{
import_assets::{ImportAssets, ImportCandidate},
import_assets::{ImportAssets, ImportCandidate, LocatedImport},
insert_use::ImportScope,
};
use itertools::Itertools;
use syntax::{AstNode, SyntaxNode, T};
use crate::{
context::CompletionContext,
context::{CompletionContext, PathKind},
render::{render_resolution_with_import, RenderContext},
ImportEdit,
};
@ -135,10 +136,35 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
&ctx.sema,
)?;
let ns_filter = |import: &LocatedImport| {
let kind = match ctx.path_kind() {
Some(kind) => kind,
None => {
return match import.original_item {
ItemInNs::Macros(mac) => mac.is_fn_like(),
_ => true,
}
}
};
match (kind, import.original_item) {
(PathKind::Expr, ItemInNs::Types(_) | ItemInNs::Values(_)) => true,
(PathKind::Type, ItemInNs::Types(_)) => true,
(PathKind::Type, ItemInNs::Values(_)) => false,
(PathKind::Expr | PathKind::Type, ItemInNs::Macros(mac)) => mac.is_fn_like(),
(PathKind::Attr, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
(PathKind::Attr, ItemInNs::Macros(mac)) => mac.is_attr(),
(PathKind::Attr, _) => false,
}
};
acc.add_all(
import_assets
.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
.into_iter()
.filter(ns_filter)
.filter(|import| {
!ctx.is_item_hidden(&import.item_to_import)
&& !ctx.is_item_hidden(&import.original_item)

View file

@ -2,6 +2,7 @@
use std::iter;
use hir::ScopeDef;
use rustc_hash::FxHashSet;
use syntax::{ast, AstNode};
@ -31,12 +32,12 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
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) {
if let hir::ScopeDef::MacroDef(macro_def) = def {
if let ScopeDef::MacroDef(macro_def) = def {
if macro_def.is_fn_like() {
acc.add_macro(ctx, Some(name.clone()), macro_def);
}
}
if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
acc.add_resolution(ctx, name, &def);
}
}
@ -44,22 +45,37 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
return;
}
Some(ImmediateLocation::Visibility(_)) => {
if let hir::PathResolution::Def(hir::ModuleDef::Module(resolved)) = resolution {
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
if let Some(current_module) = ctx.scope.module() {
if let Some(next) = current_module
.path_to_root(ctx.db)
.into_iter()
.take_while(|&it| it != resolved)
.take_while(|&it| it != module)
.next()
{
if let Some(name) = next.name(ctx.db) {
acc.add_resolution(ctx, name, &hir::ScopeDef::ModuleDef(next.into()));
acc.add_resolution(ctx, name, &ScopeDef::ModuleDef(next.into()));
}
}
}
}
return;
}
Some(ImmediateLocation::Attribute(_)) => {
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
for (name, def) in module.scope(ctx.db, context_module) {
let add_resolution = match def {
ScopeDef::MacroDef(mac) => mac.is_attr(),
ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
_ => false,
};
if add_resolution {
acc.add_resolution(ctx, name, &def);
}
}
}
return;
}
_ => (),
}
@ -91,7 +107,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
let module_scope = module.scope(ctx.db, context_module);
for (name, def) in module_scope {
if ctx.in_use_tree() {
if let hir::ScopeDef::Unknown = def {
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
@ -104,16 +120,16 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
let add_resolution = match def {
// Don't suggest attribute macros and derives.
hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(),
ScopeDef::MacroDef(mac) => mac.is_fn_like(),
// no values in type places
hir::ScopeDef::ModuleDef(
ScopeDef::ModuleDef(
hir::ModuleDef::Function(_)
| hir::ModuleDef::Variant(_)
| hir::ModuleDef::Static(_),
)
| hir::ScopeDef::Local(_) => !ctx.expects_type(),
| ScopeDef::Local(_) => !ctx.expects_type(),
// unless its a constant in a generic arg list position
hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(_)) => {
ScopeDef::ModuleDef(hir::ModuleDef::Const(_)) => {
!ctx.expects_type() || ctx.expects_generic_arg()
}
_ => true,

View file

@ -56,6 +56,19 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
});
return;
}
Some(ImmediateLocation::Attribute(_)) => {
ctx.process_all_names(&mut |name, res| {
let add_resolution = match res {
ScopeDef::MacroDef(mac) => mac.is_attr(),
ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
_ => false,
};
if add_resolution {
acc.add_resolution(ctx, name, &res);
}
});
return;
}
_ => (),
}

View file

@ -30,10 +30,11 @@ pub(crate) enum PatternRefutability {
Irrefutable,
}
#[derive(Debug)]
#[derive(Copy, Clone, Debug)]
pub(super) enum PathKind {
Expr,
Type,
Attr,
}
#[derive(Debug)]
@ -232,8 +233,7 @@ impl<'a> CompletionContext<'a> {
}
pub(crate) fn is_path_disallowed(&self) -> bool {
self.attribute_under_caret.is_some()
|| self.previous_token_is(T![unsafe])
self.previous_token_is(T![unsafe])
|| matches!(
self.prev_sibling,
Some(ImmediatePrevSibling::Attribute | ImmediatePrevSibling::Visibility)
@ -241,8 +241,7 @@ impl<'a> CompletionContext<'a> {
|| matches!(
self.completion_location,
Some(
ImmediateLocation::Attribute(_)
| ImmediateLocation::ModDeclaration(_)
ImmediateLocation::ModDeclaration(_)
| ImmediateLocation::RecordPat(_)
| ImmediateLocation::RecordExpr(_)
| ImmediateLocation::Rename
@ -274,6 +273,10 @@ impl<'a> CompletionContext<'a> {
self.path_context.as_ref().and_then(|it| it.qualifier.as_ref())
}
pub(crate) fn path_kind(&self) -> Option<PathKind> {
self.path_context.as_ref().and_then(|it| it.kind)
}
/// Checks if an item is visible and not `doc(hidden)` at the completion site.
pub(crate) fn is_visible<I>(&self, item: &I) -> bool
where
@ -785,6 +788,7 @@ impl<'a> CompletionContext<'a> {
match parent {
ast::PathType(_it) => Some(PathKind::Type),
ast::PathExpr(_it) => Some(PathKind::Expr),
ast::Meta(_it) => Some(PathKind::Attr),
_ => None,
}
};

View file

@ -1,8 +1,12 @@
//! Renderer for macro invocations.
use either::Either;
use hir::HasSource;
use ide_db::SymbolKind;
use syntax::{display::macro_label, SmolStr};
use syntax::{
display::{fn_as_proc_macro_label, macro_label},
SmolStr,
};
use crate::{
context::CallKind,
@ -35,7 +39,8 @@ impl<'a> MacroRender<'a> {
let name = name.to_smol_str();
let docs = ctx.docs(macro_);
let docs_str = docs.as_ref().map_or("", |s| s.as_str());
let (bra, ket) = guess_macro_braces(&name, docs_str);
let (bra, ket) =
if macro_.is_fn_like() { guess_macro_braces(&name, docs_str) } else { ("", "") };
MacroRender { ctx, name, macro_, docs, bra, ket }
}
@ -47,15 +52,23 @@ impl<'a> MacroRender<'a> {
} else {
Some(self.ctx.source_range())
}?;
let mut item = CompletionItem::new(SymbolKind::Macro, source_range, self.label());
let kind = match self.macro_.kind() {
hir::MacroKind::Derive => SymbolKind::Derive,
hir::MacroKind::Attr => SymbolKind::Attribute,
hir::MacroKind::BuiltIn | hir::MacroKind::Declarative | hir::MacroKind::ProcMacro => {
SymbolKind::Macro
}
};
let mut item = CompletionItem::new(kind, source_range, self.label());
item.set_deprecated(self.ctx.is_deprecated(self.macro_)).set_detail(self.detail());
if let Some(import_to_add) = import_to_add {
item.add_import(import_to_add);
}
let needs_bang = !(self.ctx.completion.in_use_tree()
|| matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac)));
let needs_bang = self.macro_.is_fn_like()
&& !(self.ctx.completion.in_use_tree()
|| matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac)));
let has_parens = self.ctx.completion.path_call_kind().is_some();
match self.ctx.snippet_cap() {
@ -84,10 +97,10 @@ impl<'a> MacroRender<'a> {
}
fn label(&self) -> SmolStr {
if self.needs_bang() && self.ctx.snippet_cap().is_some() {
SmolStr::from_iter([&*self.name, "!", self.bra, "", self.ket])
} else if self.macro_.kind() == hir::MacroKind::Derive {
if !self.macro_.is_fn_like() {
self.name.clone()
} else if self.needs_bang() && self.ctx.snippet_cap().is_some() {
SmolStr::from_iter([&*self.name, "!", self.bra, "", self.ket])
} else {
self.banged_name()
}
@ -98,8 +111,11 @@ impl<'a> MacroRender<'a> {
}
fn detail(&self) -> Option<String> {
let ast_node = self.macro_.source(self.ctx.db())?.value.left()?;
Some(macro_label(&ast_node))
let detail = match self.macro_.source(self.ctx.db())?.value {
Either::Left(node) => macro_label(&node),
Either::Right(node) => fn_as_proc_macro_label(&node),
};
Some(detail)
}
}

View file

@ -9,12 +9,12 @@ fn check(ra_fixture: &str, expect: Expect) {
}
#[test]
fn doesnt_complete_items() {
fn proc_macros() {
check(
r#"
struct Foo;
//- proc_macros: identity
#[$0]
use self as this;
struct Foo;
"#,
expect![[r#"
at allow()
@ -29,19 +29,29 @@ use self as this;
at doc(alias = "")
at must_use
at no_mangle
at derive()
at repr()
at non_exhaustive
kw self
kw super
kw crate
md proc_macros
"#]],
)
}
#[test]
fn doesnt_complete_qualified() {
fn proc_macros_qualified() {
check(
r#"
//- proc_macros: identity
#[proc_macros::$0]
struct Foo;
#[foo::$0]
use self as this;
"#,
expect![[r#""#]],
expect![[r#"
at input_replace pub macro input_replace
at identity pub macro identity
"#]],
)
}
@ -61,6 +71,9 @@ fn with_existing_attr() {
at deny()
at forbid()
at warn()
kw self
kw super
kw crate
"#]],
)
}
@ -90,6 +103,9 @@ fn attr_on_source_file() {
at recursion_limit = ""
at type_length_limit =
at windows_subsystem = ""
kw self
kw super
kw crate
"#]],
);
}
@ -113,6 +129,9 @@ fn attr_on_module() {
at no_mangle
at macro_use
at path = ""
kw self
kw super
kw crate
"#]],
);
check(
@ -131,6 +150,9 @@ fn attr_on_module() {
at must_use
at no_mangle
at no_implicit_prelude
kw self
kw super
kw crate
"#]],
);
}
@ -154,6 +176,9 @@ fn attr_on_macro_rules() {
at no_mangle
at macro_export
at macro_use
kw self
kw super
kw crate
"#]],
);
}
@ -175,6 +200,9 @@ fn attr_on_macro_def() {
at doc(alias = "")
at must_use
at no_mangle
kw self
kw super
kw crate
"#]],
);
}
@ -197,6 +225,9 @@ fn attr_on_extern_crate() {
at must_use
at no_mangle
at macro_use
kw self
kw super
kw crate
"#]],
);
}
@ -218,6 +249,9 @@ fn attr_on_use() {
at doc(alias = "")
at must_use
at no_mangle
kw self
kw super
kw crate
"#]],
);
}
@ -239,6 +273,9 @@ fn attr_on_type_alias() {
at doc(alias = "")
at must_use
at no_mangle
kw self
kw super
kw crate
"#]],
);
}
@ -263,6 +300,9 @@ fn attr_on_struct() {
at derive()
at repr()
at non_exhaustive
kw self
kw super
kw crate
"#]],
);
}
@ -287,6 +327,9 @@ fn attr_on_enum() {
at derive()
at repr()
at non_exhaustive
kw self
kw super
kw crate
"#]],
);
}
@ -308,6 +351,9 @@ fn attr_on_const() {
at doc(alias = "")
at must_use
at no_mangle
kw self
kw super
kw crate
"#]],
);
}
@ -334,6 +380,9 @@ fn attr_on_static() {
at link_section = ""
at global_allocator
at used
kw self
kw super
kw crate
"#]],
);
}
@ -356,6 +405,9 @@ fn attr_on_trait() {
at must_use
at no_mangle
at must_use
kw self
kw super
kw crate
"#]],
);
}
@ -378,6 +430,9 @@ fn attr_on_impl() {
at must_use
at no_mangle
at automatically_derived
kw self
kw super
kw crate
"#]],
);
check(
@ -395,6 +450,9 @@ fn attr_on_impl() {
at doc(alias = "")
at must_use
at no_mangle
kw self
kw super
kw crate
"#]],
);
}
@ -417,6 +475,9 @@ fn attr_on_extern_block() {
at must_use
at no_mangle
at link
kw self
kw super
kw crate
"#]],
);
check(
@ -435,6 +496,9 @@ fn attr_on_extern_block() {
at must_use
at no_mangle
at link
kw self
kw super
kw crate
"#]],
);
}
@ -451,6 +515,9 @@ fn attr_on_variant() {
at forbid()
at warn()
at non_exhaustive
kw self
kw super
kw crate
"#]],
);
}
@ -487,6 +554,9 @@ fn attr_on_fn() {
at target_feature = ""
at test
at track_caller
kw self
kw super
kw crate
"#]],
);
}
@ -503,6 +573,9 @@ fn attr_on_expr() {
at deny()
at forbid()
at warn()
kw self
kw super
kw crate
"#]],
);
}
@ -548,6 +621,9 @@ fn attr_in_source_file_end() {
at track_caller
at used
at warn()
kw self
kw super
kw crate
"#]],
);
}

View file

@ -1043,3 +1043,31 @@ enum Foo {
"#]],
)
}
#[test]
fn flyimport_attribute() {
check(
r#"
//- proc_macros:identity
#[ide$0]
struct Foo;
"#,
expect![[r#"
at identity (use proc_macros::identity) pub macro identity
"#]],
);
check_edit(
"identity",
r#"
//- proc_macros:identity
#[ide$0]
struct Foo;
"#,
r#"
use proc_macros::identity;
#[identity]
struct Foo;
"#,
);
}