Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide_completion/src/context.rs')
-rw-r--r--crates/ide_completion/src/context.rs85
1 files changed, 74 insertions, 11 deletions
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index da80224dd8..51bbd66ff3 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -12,6 +12,7 @@ use ide_db::{
famous_defs::FamousDefs,
RootDatabase,
};
+use rustc_hash::FxHashSet;
use syntax::{
algo::{find_node_at_offset, non_trivia_sibling},
ast::{self, AttrKind, HasName, NameOrNameRef},
@@ -43,11 +44,12 @@ pub(crate) enum Visible {
No,
}
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(super) enum PathKind {
Expr,
Type,
Attr { kind: AttrKind, annotated_item_kind: Option<SyntaxKind> },
+ Derive,
Mac,
Pat,
Vis { has_in_token: bool },
@@ -126,7 +128,6 @@ pub(crate) struct CompletionContext<'a> {
/// The parent function of the cursor position if it exists.
pub(super) function_def: Option<ast::Fn>,
- pub(super) attr: Option<ast::Attr>,
/// The parent impl of the cursor position if it exists.
pub(super) impl_def: Option<ast::Impl>,
/// The NameLike under the cursor in the original file if it exists.
@@ -142,6 +143,8 @@ pub(crate) struct CompletionContext<'a> {
pub(super) pattern_ctx: Option<PatternContext>,
pub(super) path_context: Option<PathCompletionCtx>,
+ pub(super) existing_derives: FxHashSet<hir::Macro>,
+
pub(super) locals: Vec<(Name, Local)>,
no_completion_required: bool,
@@ -439,7 +442,6 @@ impl<'a> CompletionContext<'a> {
expected_name: None,
expected_type: None,
function_def: None,
- attr: None,
impl_def: None,
name_syntax: None,
lifetime_ctx: None,
@@ -452,6 +454,7 @@ impl<'a> CompletionContext<'a> {
locals,
incomplete_let: false,
no_completion_required: false,
+ existing_derives: Default::default(),
};
ctx.expand_and_fill(
original_file.syntax().clone(),
@@ -472,6 +475,8 @@ impl<'a> CompletionContext<'a> {
mut fake_ident_token: SyntaxToken,
) {
let _p = profile::span("CompletionContext::expand_and_fill");
+ let mut derive_ctx = None;
+
'expansion: loop {
let parent_item =
|item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
@@ -509,11 +514,45 @@ impl<'a> CompletionContext<'a> {
_ => break 'expansion,
}
}
+ let orig_tt = match find_node_at_offset::<ast::TokenTree>(&original_file, offset) {
+ Some(it) => it,
+ None => break,
+ };
+ let spec_tt = match find_node_at_offset::<ast::TokenTree>(&speculative_file, offset) {
+ Some(it) => it,
+ None => break,
+ };
+
+ // Expand pseudo-derive expansion
+ if let (Some(orig_attr), Some(spec_attr)) = (
+ orig_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
+ spec_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
+ ) {
+ match (
+ self.sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
+ self.sema.speculative_expand_derive_as_pseudo_attr_macro(
+ &orig_attr,
+ &spec_attr,
+ fake_ident_token.clone(),
+ ),
+ ) {
+ // Clearly not a derive macro
+ (None, None) => (),
+ // successful expansions
+ (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
+ let new_offset = fake_mapped_token.text_range().start();
+ derive_ctx = Some((actual_expansion, fake_expansion, new_offset));
+ break 'expansion;
+ }
+ // exactly one expansion failed, inconsistent state so stop expanding completely
+ _ => break 'expansion,
+ }
+ }
// Expand fn-like macro calls
if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
- find_node_at_offset::<ast::MacroCall>(&original_file, offset),
- find_node_at_offset::<ast::MacroCall>(&speculative_file, offset),
+ orig_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
+ spec_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
) {
let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text());
let mac_call_path1 =
@@ -553,7 +592,7 @@ impl<'a> CompletionContext<'a> {
break;
}
- self.fill(&original_file, speculative_file, offset);
+ self.fill(&original_file, speculative_file, offset, derive_ctx);
}
fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
@@ -697,6 +736,7 @@ impl<'a> CompletionContext<'a> {
original_file: &SyntaxNode,
file_with_fake_ident: SyntaxNode,
offset: TextSize,
+ derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize)>,
) {
let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
let syntax_element = NodeOrToken::Token(fake_ident_token);
@@ -708,11 +748,6 @@ impl<'a> CompletionContext<'a> {
(fn_is_prev && !inside_impl_trait_block) || for_is_prev2
};
- self.attr = self
- .sema
- .token_ancestors_with_macros(self.token.clone())
- .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
- .find_map(ast::Attr::cast);
self.fake_attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
self.incomplete_let =
@@ -724,6 +759,33 @@ impl<'a> CompletionContext<'a> {
self.expected_type = expected_type;
self.expected_name = expected_name;
+ // Overwrite the path kind for derives
+ if let Some((original_file, file_with_fake_ident, offset)) = derive_ctx {
+ let attr = self
+ .sema
+ .token_ancestors_with_macros(self.token.clone())
+ .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
+ .find_map(ast::Attr::cast);
+ if let Some(attr) = &attr {
+ self.existing_derives =
+ self.sema.resolve_derive_macro(attr).into_iter().flatten().flatten().collect();
+ }
+
+ if let Some(ast::NameLike::NameRef(name_ref)) =
+ find_node_at_offset(&file_with_fake_ident, offset)
+ {
+ self.name_syntax =
+ find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
+ if let Some((path_ctx, _)) =
+ Self::classify_name_ref(&self.sema, &original_file, name_ref)
+ {
+ self.path_context =
+ Some(PathCompletionCtx { kind: Some(PathKind::Derive), ..path_ctx });
+ }
+ }
+ return;
+ }
+
let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
Some(it) => it,
None => return,
@@ -743,6 +805,7 @@ impl<'a> CompletionContext<'a> {
.token_ancestors_with_macros(self.token.clone())
.take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
.find_map(ast::Fn::cast);
+
match name_like {
ast::NameLike::Lifetime(lifetime) => {
self.lifetime_ctx =