From 726da9884b761d9ae1ad266ab50a9bb758ccb8a9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 28 Dec 2021 16:47:21 +0300 Subject: [PATCH] avoid speculation when completing macros --- crates/hir/src/semantics.rs | 25 ++++++++++++++++++- .../src/completions/attribute.rs | 4 +-- .../src/completions/attribute/derive.rs | 13 +++------- .../ide_completion/src/completions/keyword.rs | 2 +- crates/ide_completion/src/context.rs | 14 ++++++++--- 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 02cfeb361cf..2cf63efff82 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -10,7 +10,7 @@ use hir_def::{ resolver::{self, HasResolver, Resolver, TypeNs}, AsMacroCall, FunctionId, TraitId, VariantId, }; -use hir_expand::{name::AsName, ExpansionInfo}; +use hir_expand::{name::AsName, ExpansionInfo, MacroCallLoc}; use hir_ty::{associated_type_shorthand_candidates, Interner}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; @@ -160,6 +160,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.expand_attr_macro(item) } + pub fn resolve_derive_macro(&self, derive: &ast::Attr) -> Option> { + self.imp.resolve_derive_macro(derive) + } + pub fn expand_derive_macro(&self, derive: &ast::Attr) -> Option> { self.imp.expand_derive_macro(derive) } @@ -443,6 +447,25 @@ impl<'db> SemanticsImpl<'db> { Some(node) } + fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option> { + let item = attr.syntax().parent().and_then(ast::Item::cast)?; + let file_id = self.find_file(item.syntax()).file_id; + let item = InFile::new(file_id, &item); + let src = InFile::new(file_id, attr.clone()); + self.with_ctx(|ctx| { + let macro_call_ids = ctx.attr_to_derive_macro_call(item, src)?; + + let res = macro_call_ids + .iter() + .map(|&call| { + let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call); + MacroDef { id: loc.def } + }) + .collect(); + Some(res) + }) + } + fn expand_derive_macro(&self, attr: &ast::Attr) -> Option> { let item = attr.syntax().parent().and_then(ast::Item::cast)?; let file_id = self.find_file(item.syntax()).file_id; diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs index b1347066fd3..d763878834f 100644 --- a/crates/ide_completion/src/completions/attribute.rs +++ b/crates/ide_completion/src/completions/attribute.rs @@ -26,7 +26,7 @@ mod lint; mod repr; pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - let attribute = ctx.attribute_under_caret.as_ref()?; + let attribute = ctx.fake_attribute_under_caret.as_ref()?; let name_ref = match attribute.path() { Some(p) => Some(p.as_single_name_ref()?), None => None, @@ -34,7 +34,7 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) match (name_ref, attribute.token_tree()) { (Some(path), Some(tt)) if tt.l_paren_token().is_some() => match path.text().as_str() { "repr" => repr::complete_repr(acc, ctx, tt), - "derive" => derive::complete_derive(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?), + "derive" => derive::complete_derive(acc, ctx, ctx.attr.as_ref()?), "feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES), "allow" | "warn" | "deny" | "forbid" => { let existing_lints = parse_tt_as_comma_sep_paths(tt)?; diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs index 3cab1918f3e..8aaade350f0 100644 --- a/crates/ide_completion/src/completions/attribute/derive.rs +++ b/crates/ide_completion/src/completions/attribute/derive.rs @@ -13,17 +13,10 @@ use crate::{ item::CompletionItem, Completions, ImportEdit, }; -pub(super) fn complete_derive( - acc: &mut Completions, - ctx: &CompletionContext, - existing_derives: &[ast::Path], -) { +pub(super) fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, attr: &ast::Attr) { let core = ctx.famous_defs().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(); + let existing_derives: FxHashSet<_> = + ctx.sema.resolve_derive_macro(attr).into_iter().flatten().collect(); for (name, mac) in get_derives_in_scope(ctx) { if existing_derives.contains(&mac) { diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 90e06221db2..6b013c91225 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs @@ -19,7 +19,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte cov_mark::hit!(no_keyword_completion_in_record_lit); return; } - if ctx.attribute_under_caret.is_some() { + if ctx.fake_attribute_under_caret.is_some() { cov_mark::hit!(no_keyword_completion_in_attr_of_expr); return; } diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index de67d68cdb4..6e43aa608ac 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -103,6 +103,7 @@ pub(crate) struct CompletionContext<'a> { /// The parent function of the cursor position if it exists. pub(super) function_def: Option, + pub(super) attr: Option, /// The parent impl of the cursor position if it exists. pub(super) impl_def: Option, /// The NameLike under the cursor in the original file if it exists. @@ -111,7 +112,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) completion_location: Option, pub(super) prev_sibling: Option, - pub(super) attribute_under_caret: Option, + pub(super) fake_attribute_under_caret: Option, pub(super) previous_token: Option, pub(super) lifetime_ctx: Option, @@ -397,13 +398,14 @@ impl<'a> CompletionContext<'a> { expected_name: None, expected_type: None, function_def: None, + attr: None, impl_def: None, name_syntax: None, lifetime_ctx: None, pattern_ctx: None, completion_location: None, prev_sibling: None, - attribute_under_caret: None, + fake_attribute_under_caret: None, previous_token: None, path_context: None, locals, @@ -641,7 +643,6 @@ impl<'a> CompletionContext<'a> { let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); let syntax_element = NodeOrToken::Token(fake_ident_token); self.previous_token = previous_token(syntax_element.clone()); - self.attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast); self.no_completion_required = { let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone()); let fn_is_prev = self.previous_token_is(T![fn]); @@ -649,6 +650,13 @@ 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 = syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { it.syntax().text_range().end() == syntax_element.text_range().end()