MacroKind::Attr | MacroKind::Derive => false,
}
}
+
+ pub fn is_attr(&self) -> bool {
+ matches!(self.kind(), MacroKind::Attr)
+ }
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
//! 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,
};
&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)
use std::iter;
+use hir::ScopeDef;
use rustc_hash::FxHashSet;
use syntax::{ast, AstNode};
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);
}
}
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;
+ }
_ => (),
}
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
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,
});
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;
+ }
_ => (),
}
Irrefutable,
}
-#[derive(Debug)]
+#[derive(Copy, Clone, Debug)]
pub(super) enum PathKind {
Expr,
Type,
+ Attr,
}
#[derive(Debug)]
}
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)
|| matches!(
self.completion_location,
Some(
- ImmediateLocation::Attribute(_)
- | ImmediateLocation::ModDeclaration(_)
+ ImmediateLocation::ModDeclaration(_)
| ImmediateLocation::RecordPat(_)
| ImmediateLocation::RecordExpr(_)
| ImmediateLocation::Rename
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
match parent {
ast::PathType(_it) => Some(PathKind::Type),
ast::PathExpr(_it) => Some(PathKind::Expr),
+ ast::Meta(_it) => Some(PathKind::Attr),
_ => None,
}
};
//! 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,
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 }
}
} 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() {
}
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()
}
}
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)
}
}
}
#[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(…)
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
+ "#]],
)
}
at deny(…)
at forbid(…)
at warn(…)
+ kw self
+ kw super
+ kw crate
"#]],
)
}
at recursion_limit = "…"
at type_length_limit = …
at windows_subsystem = "…"
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at no_mangle
at macro_use
at path = "…"
+ kw self
+ kw super
+ kw crate
"#]],
);
check(
at must_use
at no_mangle
at no_implicit_prelude
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at no_mangle
at macro_export
at macro_use
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at doc(alias = "…")
at must_use
at no_mangle
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at must_use
at no_mangle
at macro_use
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at doc(alias = "…")
at must_use
at no_mangle
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at doc(alias = "…")
at must_use
at no_mangle
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at derive(…)
at repr(…)
at non_exhaustive
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at derive(…)
at repr(…)
at non_exhaustive
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at doc(alias = "…")
at must_use
at no_mangle
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at link_section = "…"
at global_allocator
at used
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at must_use
at no_mangle
at must_use
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at must_use
at no_mangle
at automatically_derived
+ kw self
+ kw super
+ kw crate
"#]],
);
check(
at doc(alias = "…")
at must_use
at no_mangle
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at must_use
at no_mangle
at link
+ kw self
+ kw super
+ kw crate
"#]],
);
check(
at must_use
at no_mangle
at link
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at forbid(…)
at warn(…)
at non_exhaustive
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at target_feature = "…"
at test
at track_caller
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at deny(…)
at forbid(…)
at warn(…)
+ kw self
+ kw super
+ kw crate
"#]],
);
}
at track_caller
at used
at warn(…)
+ kw self
+ kw super
+ kw crate
"#]],
);
}
"#]],
)
}
+
+#[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;
+"#,
+ );
+}