mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 05:38:46 +00:00
Complete derive helper attributes
Only their names, anything can go inside.
This commit is contained in:
parent
e6276c8b64
commit
890d155ffe
5 changed files with 111 additions and 4 deletions
|
@ -510,6 +510,22 @@ impl<'db> SemanticsImpl<'db> {
|
|||
self.with_ctx(|ctx| ctx.has_derives(adt))
|
||||
}
|
||||
|
||||
pub fn derive_helpers_in_scope(&self, adt: &ast::Adt) -> Option<Vec<(Symbol, Symbol)>> {
|
||||
let sa = self.analyze_no_infer(adt.syntax())?;
|
||||
let id = self.db.ast_id_map(sa.file_id).ast_id(adt);
|
||||
let result = sa
|
||||
.resolver
|
||||
.def_map()
|
||||
.derive_helpers_in_scope(InFile::new(sa.file_id, id))?
|
||||
.iter()
|
||||
.map(|(name, macro_, _)| {
|
||||
let macro_name = Macro::from(*macro_).name(self.db).symbol().clone();
|
||||
(name.symbol().clone(), macro_name)
|
||||
})
|
||||
.collect();
|
||||
Some(result)
|
||||
}
|
||||
|
||||
pub fn derive_helper(&self, attr: &ast::Attr) -> Option<Vec<(Macro, MacroFileId)>> {
|
||||
let adt = attr.syntax().ancestors().find_map(ast::Item::cast).and_then(|it| match it {
|
||||
ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
|
||||
|
|
|
@ -86,10 +86,21 @@ pub(crate) fn complete_attribute_path(
|
|||
acc: &mut Completions,
|
||||
ctx: &CompletionContext<'_>,
|
||||
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
|
||||
&AttrCtx { kind, annotated_item_kind }: &AttrCtx,
|
||||
&AttrCtx { kind, annotated_item_kind, ref derive_helpers }: &AttrCtx,
|
||||
) {
|
||||
let is_inner = kind == AttrKind::Inner;
|
||||
|
||||
for (derive_helper, derive_name) in derive_helpers {
|
||||
let mut item = CompletionItem::new(
|
||||
SymbolKind::Attribute,
|
||||
ctx.source_range(),
|
||||
derive_helper.as_str(),
|
||||
ctx.edition,
|
||||
);
|
||||
item.detail(format!("derive helper of `{derive_name}`"));
|
||||
item.add_to(acc, ctx.db);
|
||||
}
|
||||
|
||||
match qualified {
|
||||
Qualified::With {
|
||||
resolution: Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))),
|
||||
|
|
|
@ -7,8 +7,8 @@ mod tests;
|
|||
use std::{iter, ops::ControlFlow};
|
||||
|
||||
use hir::{
|
||||
HasAttrs, Local, ModuleSource, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type,
|
||||
TypeInfo,
|
||||
HasAttrs, Local, ModuleSource, Name, PathResolution, ScopeDef, Semantics, SemanticsScope,
|
||||
Symbol, Type, TypeInfo,
|
||||
};
|
||||
use ide_db::{
|
||||
base_db::SourceDatabase, famous_defs::FamousDefs, helpers::is_editable_crate, FilePosition,
|
||||
|
@ -133,6 +133,7 @@ pub(crate) type ExistingDerives = FxHashSet<hir::Macro>;
|
|||
pub(crate) struct AttrCtx {
|
||||
pub(crate) kind: AttrKind,
|
||||
pub(crate) annotated_item_kind: Option<SyntaxKind>,
|
||||
pub(crate) derive_helpers: Vec<(Symbol, Symbol)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
|
|
@ -1129,7 +1129,22 @@ fn classify_name_ref(
|
|||
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 { attr_ctx: AttrCtx { kind, annotated_item_kind } })
|
||||
let derive_helpers = annotated_item_kind
|
||||
.filter(|kind| {
|
||||
matches!(
|
||||
kind,
|
||||
SyntaxKind::STRUCT
|
||||
| SyntaxKind::ENUM
|
||||
| SyntaxKind::UNION
|
||||
| SyntaxKind::VARIANT
|
||||
| SyntaxKind::TUPLE_FIELD
|
||||
| SyntaxKind::RECORD_FIELD
|
||||
)
|
||||
})
|
||||
.and_then(|_| nameref.as_ref()?.syntax().ancestors().find_map(ast::Adt::cast))
|
||||
.and_then(|adt| sema.derive_helpers_in_scope(&adt))
|
||||
.unwrap_or_default();
|
||||
Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind, derive_helpers } })
|
||||
};
|
||||
|
||||
// Infer the path kind
|
||||
|
|
|
@ -8,6 +8,70 @@ fn check(ra_fixture: &str, expect: Expect) {
|
|||
expect.assert_eq(&actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_helpers() {
|
||||
check(
|
||||
r#"
|
||||
//- /mac.rs crate:mac
|
||||
#![crate_type = "proc-macro"]
|
||||
|
||||
#[proc_macro_derive(MyDerive, attributes(my_cool_helper_attribute))]
|
||||
pub fn my_derive() {}
|
||||
|
||||
//- /lib.rs crate:lib deps:mac
|
||||
#[rustc_builtin_macro]
|
||||
pub macro derive($item:item) {}
|
||||
|
||||
#[derive(mac::MyDerive)]
|
||||
pub struct Foo(#[m$0] i32);
|
||||
"#,
|
||||
expect![[r#"
|
||||
at allow(…)
|
||||
at automatically_derived
|
||||
at cfg(…)
|
||||
at cfg_attr(…)
|
||||
at cold
|
||||
at deny(…)
|
||||
at deprecated
|
||||
at derive macro derive
|
||||
at derive(…)
|
||||
at doc = "…"
|
||||
at doc(alias = "…")
|
||||
at doc(hidden)
|
||||
at expect(…)
|
||||
at export_name = "…"
|
||||
at forbid(…)
|
||||
at global_allocator
|
||||
at ignore = "…"
|
||||
at inline
|
||||
at link
|
||||
at link_name = "…"
|
||||
at link_section = "…"
|
||||
at macro_export
|
||||
at macro_use
|
||||
at must_use
|
||||
at my_cool_helper_attribute derive helper of `MyDerive`
|
||||
at no_mangle
|
||||
at non_exhaustive
|
||||
at panic_handler
|
||||
at path = "…"
|
||||
at proc_macro
|
||||
at proc_macro_attribute
|
||||
at proc_macro_derive(…)
|
||||
at repr(…)
|
||||
at should_panic
|
||||
at target_feature(enable = "…")
|
||||
at test
|
||||
at track_caller
|
||||
at used
|
||||
at warn(…)
|
||||
md mac
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proc_macros() {
|
||||
check(
|
||||
|
|
Loading…
Reference in a new issue