Skip to content

Commit 3dfe504

Browse files
committed
Derive completions work on hir, not names
1 parent aa9d093 commit 3dfe504

File tree

3 files changed

+93
-87
lines changed

3 files changed

+93
-87
lines changed

crates/ide_completion/src/completions/attribute/derive.rs

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//! Completion for derives
2-
use hir::HasAttrs;
2+
use hir::{HasAttrs, MacroDef, MacroKind};
3+
use ide_db::helpers::FamousDefs;
34
use itertools::Itertools;
4-
use rustc_hash::FxHashMap;
5-
use syntax::{ast, SmolStr};
5+
use rustc_hash::FxHashSet;
6+
use syntax::ast;
67

78
use crate::{
89
context::CompletionContext,
@@ -15,36 +16,51 @@ pub(super) fn complete_derive(
1516
ctx: &CompletionContext,
1617
derive_input: ast::TokenTree,
1718
) {
18-
if let Some(existing_derives) = super::parse_comma_sep_paths(derive_input) {
19-
for (derive, docs) in get_derive_names_in_scope(ctx) {
19+
if let Some(existing_derives) = super::parse_comma_sep_paths(derive_input.clone()) {
20+
let core = FamousDefs(&ctx.sema, ctx.krate).core();
21+
let existing_derives: FxHashSet<_> = existing_derives
22+
.into_iter()
23+
.filter_map(|path| ctx.scope.speculative_resolve_as_mac(&path))
24+
.filter(|mac| mac.kind() == MacroKind::Derive)
25+
.collect();
26+
27+
for (name, mac) in get_derives_in_scope(ctx) {
28+
if existing_derives.contains(&mac) {
29+
continue;
30+
}
31+
32+
let name = name.to_smol_str();
2033
let label;
21-
let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS
22-
.iter()
23-
.find(|derive_completion| derive_completion.label == derive)
24-
{
25-
let mut components = vec![derive_completion.label];
26-
components.extend(derive_completion.dependencies.iter().filter(|&&dependency| {
27-
!existing_derives
34+
let (label, lookup) = match core.zip(mac.module(ctx.db).map(|it| it.krate())) {
35+
// show derive dependencies for `core`/`std` derives
36+
Some((core, mac_krate)) if core == mac_krate => {
37+
if let Some(derive_completion) = DEFAULT_DERIVE_DEPENDENCIES
2838
.iter()
29-
.filter_map(|it| it.as_single_name_ref())
30-
.any(|it| it.text() == dependency)
31-
}));
32-
let lookup = components.join(", ");
33-
label = components.iter().rev().join(", ");
34-
(&*label, Some(lookup))
35-
} else if existing_derives
36-
.iter()
37-
.filter_map(|it| it.as_single_name_ref())
38-
.any(|it| it.text().as_str() == derive)
39-
{
40-
continue;
41-
} else {
42-
(&*derive, None)
39+
.find(|derive_completion| derive_completion.label == name)
40+
{
41+
let mut components = vec![derive_completion.label];
42+
components.extend(derive_completion.dependencies.iter().filter(
43+
|&&dependency| {
44+
!existing_derives
45+
.iter()
46+
.filter_map(|it| it.name(ctx.db))
47+
.any(|it| it.to_smol_str() == dependency)
48+
},
49+
));
50+
let lookup = components.join(", ");
51+
label = components.iter().rev().join(", ");
52+
(label.as_str(), Some(lookup))
53+
} else {
54+
(&*name, None)
55+
}
56+
}
57+
_ => (&*name, None),
4358
};
59+
4460
let mut item =
4561
CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
4662
item.kind(CompletionItemKind::Attribute);
47-
if let Some(docs) = docs {
63+
if let Some(docs) = mac.docs(ctx.db) {
4864
item.documentation(docs);
4965
}
5066
if let Some(lookup) = lookup {
@@ -55,14 +71,12 @@ pub(super) fn complete_derive(
5571
}
5672
}
5773

58-
fn get_derive_names_in_scope(
59-
ctx: &CompletionContext,
60-
) -> FxHashMap<SmolStr, Option<hir::Documentation>> {
61-
let mut result = FxHashMap::default();
74+
fn get_derives_in_scope(ctx: &CompletionContext) -> Vec<(hir::Name, MacroDef)> {
75+
let mut result = Vec::default();
6276
ctx.process_all_names(&mut |name, scope_def| {
6377
if let hir::ScopeDef::MacroDef(mac) = scope_def {
6478
if mac.kind() == hir::MacroKind::Derive {
65-
result.insert(name.to_smol_str(), mac.docs(ctx.db));
79+
result.push((name, mac));
6680
}
6781
}
6882
});
@@ -76,7 +90,7 @@ struct DeriveDependencies {
7690

7791
/// Standard Rust derives that have dependencies
7892
/// (the dependencies are needed so that the main derive don't break the compilation when added)
79-
const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[
93+
const DEFAULT_DERIVE_DEPENDENCIES: &[DeriveDependencies] = &[
8094
DeriveDependencies { label: "Copy", dependencies: &["Clone"] },
8195
DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] },
8296
DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },

crates/ide_completion/src/tests/attribute.rs

Lines changed: 42 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -571,85 +571,73 @@ mod derive {
571571
use super::*;
572572

573573
fn check_derive(ra_fixture: &str, expect: Expect) {
574-
let builtin_derives = r#"
575-
#[rustc_builtin_macro]
576-
pub macro Clone {}
577-
#[rustc_builtin_macro]
578-
pub macro Copy {}
579-
#[rustc_builtin_macro]
580-
pub macro Default {}
581-
#[rustc_builtin_macro]
582-
pub macro Debug {}
583-
#[rustc_builtin_macro]
584-
pub macro Hash {}
585-
#[rustc_builtin_macro]
586-
pub macro PartialEq {}
587-
#[rustc_builtin_macro]
588-
pub macro Eq {}
589-
#[rustc_builtin_macro]
590-
pub macro PartialOrd {}
591-
#[rustc_builtin_macro]
592-
pub macro Ord {}
593-
594-
"#;
595-
let actual = completion_list(&format!("{} {}", builtin_derives, ra_fixture));
574+
let actual = completion_list(ra_fixture);
596575
expect.assert_eq(&actual);
597576
}
598577

599578
#[test]
600579
fn no_completion_for_incorrect_derive() {
601-
check_derive(r#"#[derive{$0)] struct Test;"#, expect![[]])
580+
check_derive(
581+
r#"
582+
//- minicore: derive, copy, clone, ord, eq, default, fmt
583+
#[derive{$0)] struct Test;
584+
"#,
585+
expect![[]],
586+
)
602587
}
603588

604589
#[test]
605590
fn empty_derive() {
606591
check_derive(
607-
r#"#[derive($0)] struct Test;"#,
592+
r#"
593+
//- minicore: derive, copy, clone, ord, eq, default, fmt
594+
#[derive($0)] struct Test;
595+
"#,
608596
expect![[r#"
609-
at PartialEq
610-
at Default
611-
at PartialEq, Eq
612-
at PartialEq, Eq, PartialOrd, Ord
613-
at Clone, Copy
614-
at Debug
615-
at Clone
616-
at Hash
617-
at PartialEq, PartialOrd
618-
"#]],
597+
at Default
598+
at Clone, Copy
599+
at PartialEq
600+
at PartialEq, Eq
601+
at PartialEq, Eq, PartialOrd, Ord
602+
at Clone
603+
at PartialEq, PartialOrd
604+
"#]],
619605
);
620606
}
621607

622608
#[test]
623609
fn derive_with_input_before() {
624610
check_derive(
625-
r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#,
611+
r#"
612+
//- minicore: derive, copy, clone, ord, eq, default, fmt
613+
#[derive(serde::Serialize, PartialEq, $0)] struct Test;
614+
"#,
626615
expect![[r#"
627-
at Default
628-
at Eq
629-
at Eq, PartialOrd, Ord
630-
at Clone, Copy
631-
at Debug
632-
at Clone
633-
at Hash
634-
at PartialOrd
635-
"#]],
616+
at Default
617+
at Clone, Copy
618+
at Eq
619+
at Eq, PartialOrd, Ord
620+
at Clone
621+
at PartialOrd
622+
"#]],
636623
)
637624
}
638625

639626
#[test]
640627
fn derive_with_input_after() {
641628
check_derive(
642-
r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#,
629+
r#"
630+
//- minicore: derive, copy, clone, ord, eq, default, fmt
631+
#[derive($0 serde::Serialize, PartialEq)] struct Test;
632+
"#,
643633
expect![[r#"
644-
at Default
645-
at Eq
646-
at Eq, PartialOrd, Ord
647-
at Clone, Copy
648-
at Debug
649-
at Clone
650-
at Hash
651-
at PartialOrd
652-
"#]],
634+
at Default
635+
at Clone, Copy
636+
at Eq
637+
at Eq, PartialOrd, Ord
638+
at Clone
639+
at PartialOrd
640+
"#]],
653641
)
654642
}
655643
}

crates/test_utils/src/minicore.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ pub mod default {
8787
pub trait Default: Sized {
8888
fn default() -> Self;
8989
}
90+
// region:derive
91+
#[rustc_builtin_macro]
92+
pub macro Default($item:item) {}
93+
// endregion:derive
9094
}
9195
// endregion:default
9296

0 commit comments

Comments
 (0)