mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
Merge #10943
10943: feat: Enable completions for attributes r=Veykril a=Veykril Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
6f84bbfa1e
8 changed files with 217 additions and 34 deletions
|
@ -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)]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue