diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs index 36758baafc..2824b2a2c2 100644 --- a/crates/ide_completion/src/completions/attribute/derive.rs +++ b/crates/ide_completion/src/completions/attribute/derive.rs @@ -1,8 +1,9 @@ //! Completion for derives -use hir::HasAttrs; +use hir::{HasAttrs, MacroDef, MacroKind}; +use ide_db::helpers::FamousDefs; use itertools::Itertools; -use rustc_hash::FxHashMap; -use syntax::{ast, SmolStr}; +use rustc_hash::FxHashSet; +use syntax::ast; use crate::{ context::CompletionContext, @@ -15,36 +16,51 @@ pub(super) fn complete_derive( ctx: &CompletionContext, derive_input: ast::TokenTree, ) { - if let Some(existing_derives) = super::parse_comma_sep_paths(derive_input) { - for (derive, docs) in get_derive_names_in_scope(ctx) { - let label; - let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS - .iter() - .find(|derive_completion| derive_completion.label == derive) - { - let mut components = vec![derive_completion.label]; - components.extend(derive_completion.dependencies.iter().filter(|&&dependency| { - !existing_derives - .iter() - .filter_map(|it| it.as_single_name_ref()) - .any(|it| it.text() == dependency) - })); - let lookup = components.join(", "); - label = components.iter().rev().join(", "); - (&*label, Some(lookup)) - } else if existing_derives - .iter() - .filter_map(|it| it.as_single_name_ref()) - .any(|it| it.text().as_str() == derive) - { + if let Some(existing_derives) = super::parse_comma_sep_paths(derive_input.clone()) { + let core = FamousDefs(&ctx.sema, ctx.krate).core(); + let existing_derives: FxHashSet<_> = existing_derives + .into_iter() + .filter_map(|path| ctx.scope.speculative_resolve_as_mac(&path)) + .filter(|mac| mac.kind() == MacroKind::Derive) + .collect(); + + for (name, mac) in get_derives_in_scope(ctx) { + if existing_derives.contains(&mac) { continue; - } else { - (&*derive, None) + } + + let name = name.to_smol_str(); + let label; + let (label, lookup) = match core.zip(mac.module(ctx.db).map(|it| it.krate())) { + // show derive dependencies for `core`/`std` derives + Some((core, mac_krate)) if core == mac_krate => { + if let Some(derive_completion) = DEFAULT_DERIVE_DEPENDENCIES + .iter() + .find(|derive_completion| derive_completion.label == name) + { + let mut components = vec![derive_completion.label]; + components.extend(derive_completion.dependencies.iter().filter( + |&&dependency| { + !existing_derives + .iter() + .filter_map(|it| it.name(ctx.db)) + .any(|it| it.to_smol_str() == dependency) + }, + )); + let lookup = components.join(", "); + label = components.iter().rev().join(", "); + (label.as_str(), Some(lookup)) + } else { + (&*name, None) + } + } + _ => (&*name, None), }; + let mut item = CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); item.kind(CompletionItemKind::Attribute); - if let Some(docs) = docs { + if let Some(docs) = mac.docs(ctx.db) { item.documentation(docs); } if let Some(lookup) = lookup { @@ -55,14 +71,12 @@ pub(super) fn complete_derive( } } -fn get_derive_names_in_scope( - ctx: &CompletionContext, -) -> FxHashMap> { - let mut result = FxHashMap::default(); +fn get_derives_in_scope(ctx: &CompletionContext) -> Vec<(hir::Name, MacroDef)> { + let mut result = Vec::default(); ctx.process_all_names(&mut |name, scope_def| { if let hir::ScopeDef::MacroDef(mac) = scope_def { if mac.kind() == hir::MacroKind::Derive { - result.insert(name.to_smol_str(), mac.docs(ctx.db)); + result.push((name, mac)); } } }); @@ -76,7 +90,7 @@ struct DeriveDependencies { /// Standard Rust derives that have dependencies /// (the dependencies are needed so that the main derive don't break the compilation when added) -const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[ +const DEFAULT_DERIVE_DEPENDENCIES: &[DeriveDependencies] = &[ DeriveDependencies { label: "Copy", dependencies: &["Clone"] }, DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] }, DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, diff --git a/crates/ide_completion/src/tests/attribute.rs b/crates/ide_completion/src/tests/attribute.rs index 9d22bb196b..6a37b53cf5 100644 --- a/crates/ide_completion/src/tests/attribute.rs +++ b/crates/ide_completion/src/tests/attribute.rs @@ -571,85 +571,73 @@ mod derive { use super::*; fn check_derive(ra_fixture: &str, expect: Expect) { - let builtin_derives = r#" - #[rustc_builtin_macro] - pub macro Clone {} - #[rustc_builtin_macro] - pub macro Copy {} - #[rustc_builtin_macro] - pub macro Default {} - #[rustc_builtin_macro] - pub macro Debug {} - #[rustc_builtin_macro] - pub macro Hash {} - #[rustc_builtin_macro] - pub macro PartialEq {} - #[rustc_builtin_macro] - pub macro Eq {} - #[rustc_builtin_macro] - pub macro PartialOrd {} - #[rustc_builtin_macro] - pub macro Ord {} - - "#; - let actual = completion_list(&format!("{} {}", builtin_derives, ra_fixture)); + let actual = completion_list(ra_fixture); expect.assert_eq(&actual); } #[test] fn no_completion_for_incorrect_derive() { - check_derive(r#"#[derive{$0)] struct Test;"#, expect![[]]) + check_derive( + r#" +//- minicore: derive, copy, clone, ord, eq, default, fmt +#[derive{$0)] struct Test; +"#, + expect![[]], + ) } #[test] fn empty_derive() { check_derive( - r#"#[derive($0)] struct Test;"#, + r#" +//- minicore: derive, copy, clone, ord, eq, default, fmt +#[derive($0)] struct Test; +"#, expect![[r#" - at PartialEq - at Default - at PartialEq, Eq - at PartialEq, Eq, PartialOrd, Ord - at Clone, Copy - at Debug - at Clone - at Hash - at PartialEq, PartialOrd - "#]], + at Default + at Clone, Copy + at PartialEq + at PartialEq, Eq + at PartialEq, Eq, PartialOrd, Ord + at Clone + at PartialEq, PartialOrd + "#]], ); } #[test] fn derive_with_input_before() { check_derive( - r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, + r#" +//- minicore: derive, copy, clone, ord, eq, default, fmt +#[derive(serde::Serialize, PartialEq, $0)] struct Test; +"#, expect![[r#" - at Default - at Eq - at Eq, PartialOrd, Ord - at Clone, Copy - at Debug - at Clone - at Hash - at PartialOrd - "#]], + at Default + at Clone, Copy + at Eq + at Eq, PartialOrd, Ord + at Clone + at PartialOrd + "#]], ) } #[test] fn derive_with_input_after() { check_derive( - r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, + r#" +//- minicore: derive, copy, clone, ord, eq, default, fmt +#[derive($0 serde::Serialize, PartialEq)] struct Test; +"#, expect![[r#" - at Default - at Eq - at Eq, PartialOrd, Ord - at Clone, Copy - at Debug - at Clone - at Hash - at PartialOrd - "#]], + at Default + at Clone, Copy + at Eq + at Eq, PartialOrd, Ord + at Clone + at PartialOrd + "#]], ) } } diff --git a/crates/test_utils/src/minicore.rs b/crates/test_utils/src/minicore.rs index 045b4898e5..ef38a194bc 100644 --- a/crates/test_utils/src/minicore.rs +++ b/crates/test_utils/src/minicore.rs @@ -87,6 +87,10 @@ pub mod default { pub trait Default: Sized { fn default() -> Self; } + // region:derive + #[rustc_builtin_macro] + pub macro Default($item:item) {} + // endregion:derive } // endregion:default