about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAleksey Kladov <aleksey.kladov@gmail.com>2021-12-28 16:47:21 +0300
committerAleksey Kladov <aleksey.kladov@gmail.com>2021-12-28 16:52:15 +0300
commit726da9884b761d9ae1ad266ab50a9bb758ccb8a9 (patch)
treeb5c43d32ff3c1d5898794e86e54c24f75f6cebe3
parent5c11b363df143b60a8fe407ce5fcce254dff4e7e (diff)
downloadrust-726da9884b761d9ae1ad266ab50a9bb758ccb8a9.tar.gz
rust-726da9884b761d9ae1ad266ab50a9bb758ccb8a9.zip
avoid speculation when completing macros
-rw-r--r--crates/hir/src/semantics.rs25
-rw-r--r--crates/ide_completion/src/completions/attribute.rs4
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs13
-rw-r--r--crates/ide_completion/src/completions/keyword.rs2
-rw-r--r--crates/ide_completion/src/context.rs14
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<Vec<MacroDef>> {
+        self.imp.resolve_derive_macro(derive)
+    }
+
     pub fn expand_derive_macro(&self, derive: &ast::Attr) -> Option<Vec<SyntaxNode>> {
         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<Vec<MacroDef>> {
+        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<Vec<SyntaxNode>> {
         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<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.
@@ -111,7 +112,7 @@ pub(crate) struct CompletionContext<'a> {
 
     pub(super) completion_location: Option<ImmediateLocation>,
     pub(super) prev_sibling: Option<ImmediatePrevSibling>,
-    pub(super) attribute_under_caret: Option<ast::Attr>,
+    pub(super) fake_attribute_under_caret: Option<ast::Attr>,
     pub(super) previous_token: Option<SyntaxToken>,
 
     pub(super) lifetime_ctx: Option<LifetimeContext>,
@@ -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()