about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2021-10-17 12:44:41 +0200
committerLukas Wirth <lukastw97@gmail.com>2021-10-17 12:44:44 +0200
commitce47d131010f7e91d76f4de2fd9e69a920bbcf23 (patch)
tree31b5a189703ba8ac8a193d0a3b2e5a7ce84f4b3a
parente329b7742b343413ad248973c3764061d363b76e (diff)
downloadrust-ce47d131010f7e91d76f4de2fd9e69a920bbcf23.tar.gz
rust-ce47d131010f7e91d76f4de2fd9e69a920bbcf23.zip
Make attribute completions more ast based
-rw-r--r--crates/ide_completion/src/completions/attribute.rs54
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs31
-rw-r--r--crates/ide_completion/src/completions/attribute/lint.rs49
-rw-r--r--crates/ide_completion/src/completions/attribute/repr.rs39
4 files changed, 110 insertions, 63 deletions
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index 2b130cecf5c..6abc2a94c41 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -5,9 +5,10 @@
 
 use hir::HasAttrs;
 use ide_db::helpers::generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES};
+use itertools::Itertools;
 use once_cell::sync::Lazy;
-use rustc_hash::{FxHashMap, FxHashSet};
-use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, NodeOrToken, SyntaxKind, T};
+use rustc_hash::FxHashMap;
+use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, SyntaxKind, T};
 
 use crate::{
     context::CompletionContext,
@@ -303,31 +304,38 @@ const ATTRIBUTES: &[AttrCompletion] = &[
     .prefer_inner(),
 ];
 
-fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Option<FxHashSet<String>> {
-    let (l_paren, r_paren) = derive_input.l_paren_token().zip(derive_input.r_paren_token())?;
-    let mut input_derives = FxHashSet::default();
-    let mut tokens = derive_input
+fn parse_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Path>> {
+    let r_paren = input.r_paren_token()?;
+    let tokens = input
         .syntax()
         .children_with_tokens()
-        .filter_map(NodeOrToken::into_token)
-        .skip_while(|token| token != &l_paren)
         .skip(1)
-        .take_while(|token| token != &r_paren)
-        .peekable();
-    let mut input = String::new();
-    while tokens.peek().is_some() {
-        for token in tokens.by_ref().take_while(|t| t.kind() != T![,]) {
-            input.push_str(token.text());
-        }
-
-        if !input.is_empty() {
-            input_derives.insert(input.trim().to_owned());
-        }
-
-        input.clear();
-    }
+        .take_while(|it| it.as_token() != Some(&r_paren));
+    let input_expressions = tokens.into_iter().group_by(|tok| tok.kind() == T![,]);
+    Some(
+        input_expressions
+            .into_iter()
+            .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
+            .filter_map(|mut tokens| ast::Path::parse(&tokens.join("")).ok())
+            .collect::<Vec<ast::Path>>(),
+    )
+}
 
-    Some(input_derives)
+fn parse_comma_sep_expr(input: ast::TokenTree) -> Option<Vec<ast::Expr>> {
+    let r_paren = input.r_paren_token()?;
+    let tokens = input
+        .syntax()
+        .children_with_tokens()
+        .skip(1)
+        .take_while(|it| it.as_token() != Some(&r_paren));
+    let input_expressions = tokens.into_iter().group_by(|tok| tok.kind() == T![,]);
+    Some(
+        input_expressions
+            .into_iter()
+            .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
+            .filter_map(|mut tokens| ast::Expr::parse(&tokens.join("")).ok())
+            .collect::<Vec<ast::Expr>>(),
+    )
 }
 
 #[test]
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
index b53824aec6c..36758baafc0 100644
--- a/crates/ide_completion/src/completions/attribute/derive.rs
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -2,7 +2,7 @@
 use hir::HasAttrs;
 use itertools::Itertools;
 use rustc_hash::FxHashMap;
-use syntax::ast;
+use syntax::{ast, SmolStr};
 
 use crate::{
     context::CompletionContext,
@@ -15,26 +15,31 @@ pub(super) fn complete_derive(
     ctx: &CompletionContext,
     derive_input: ast::TokenTree,
 ) {
-    if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) {
+    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
+                components.extend(derive_completion.dependencies.iter().filter(|&&dependency| {
+                    !existing_derives
                         .iter()
-                        .filter(|&&dependency| !existing_derives.contains(dependency)),
-                );
+                        .filter_map(|it| it.as_single_name_ref())
+                        .any(|it| it.text() == dependency)
+                }));
                 let lookup = components.join(", ");
-                let label = components.iter().rev().join(", ");
-                (label, Some(lookup))
-            } else if existing_derives.contains(&derive) {
+                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)
+            {
                 continue;
             } else {
-                (derive, None)
+                (&*derive, None)
             };
             let mut item =
                 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
@@ -52,12 +57,12 @@ pub(super) fn complete_derive(
 
 fn get_derive_names_in_scope(
     ctx: &CompletionContext,
-) -> FxHashMap<String, Option<hir::Documentation>> {
+) -> FxHashMap<SmolStr, Option<hir::Documentation>> {
     let mut result = FxHashMap::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_string(), mac.docs(ctx.db));
+                result.insert(name.to_smol_str(), mac.docs(ctx.db));
             }
         }
     });
diff --git a/crates/ide_completion/src/completions/attribute/lint.rs b/crates/ide_completion/src/completions/attribute/lint.rs
index 8b17be09b1c..dffb2d92d38 100644
--- a/crates/ide_completion/src/completions/attribute/lint.rs
+++ b/crates/ide_completion/src/completions/attribute/lint.rs
@@ -14,17 +14,46 @@ pub(super) fn complete_lint(
     derive_input: ast::TokenTree,
     lints_completions: &[Lint],
 ) {
-    if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) {
-        for lint_completion in
-            lints_completions.iter().filter(|completion| !existing_lints.contains(completion.label))
-        {
-            let mut item = CompletionItem::new(
-                CompletionKind::Attribute,
-                ctx.source_range(),
-                lint_completion.label,
-            );
+    if let Some(existing_lints) = super::parse_comma_sep_paths(derive_input) {
+        for &Lint { label, description } in lints_completions {
+            let (ex_q, ex_name) = {
+                // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead?
+                let mut parts = label.split("::");
+                let ns_or_label = match parts.next() {
+                    Some(it) => it,
+                    None => continue,
+                };
+                let label = parts.next();
+                match label {
+                    Some(label) => (Some(ns_or_label), label),
+                    None => (None, ns_or_label),
+                }
+            };
+            let repr_already_annotated = existing_lints
+                .iter()
+                .filter_map(|path| {
+                    let q = path.qualifier();
+                    if q.as_ref().and_then(|it| it.qualifier()).is_some() {
+                        return None;
+                    }
+                    Some((q.and_then(|it| it.as_single_name_ref()), path.segment()?.name_ref()?))
+                })
+                .any(|(q, name)| {
+                    let qualifier_matches = match (q, ex_q) {
+                        (None, None) => true,
+                        (None, Some(_)) => false,
+                        (Some(_), None) => false,
+                        (Some(q), Some(ns)) => q.text() == ns,
+                    };
+                    qualifier_matches && name.text() == ex_name
+                });
+            if repr_already_annotated {
+                continue;
+            }
+            let mut item =
+                CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), ex_name);
             item.kind(CompletionItemKind::Attribute)
-                .documentation(hir::Documentation::new(lint_completion.description.to_owned()));
+                .documentation(hir::Documentation::new(description.to_owned()));
             item.add_to(acc)
         }
     }
diff --git a/crates/ide_completion/src/completions/attribute/repr.rs b/crates/ide_completion/src/completions/attribute/repr.rs
index 3df04826d44..95efe77c06d 100644
--- a/crates/ide_completion/src/completions/attribute/repr.rs
+++ b/crates/ide_completion/src/completions/attribute/repr.rs
@@ -8,29 +8,34 @@ use crate::{
     Completions,
 };
 
-pub(super) fn complete_repr(
-    acc: &mut Completions,
-    ctx: &CompletionContext,
-    derive_input: ast::TokenTree,
-) {
-    if let Some(existing_reprs) = super::parse_comma_sep_input(derive_input) {
-        for repr_completion in REPR_COMPLETIONS {
-            if existing_reprs
+pub(super) fn complete_repr(acc: &mut Completions, ctx: &CompletionContext, input: ast::TokenTree) {
+    if let Some(existing_reprs) = super::parse_comma_sep_expr(input) {
+        for &ReprCompletion { label, snippet, lookup, collides } in REPR_COMPLETIONS {
+            let repr_already_annotated = existing_reprs
                 .iter()
-                .any(|it| repr_completion.label == it || repr_completion.collides.contains(&&**it))
-            {
+                .filter_map(|expr| match expr {
+                    ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
+                    ast::Expr::CallExpr(call) => match call.expr()? {
+                        ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
+                        _ => return None,
+                    },
+                    _ => None,
+                })
+                .any(|it| {
+                    let text = it.text();
+                    label == text || collides.contains(&text.as_str())
+                });
+            if repr_already_annotated {
                 continue;
             }
-            let mut item = CompletionItem::new(
-                CompletionKind::Attribute,
-                ctx.source_range(),
-                repr_completion.label,
-            );
+
+            let mut item =
+                CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
             item.kind(CompletionItemKind::Attribute);
-            if let Some(lookup) = repr_completion.lookup {
+            if let Some(lookup) = lookup {
                 item.lookup_by(lookup);
             }
-            if let Some((snippet, cap)) = repr_completion.snippet.zip(ctx.config.snippet_cap) {
+            if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) {
                 item.insert_snippet(cap, snippet);
             }
             item.add_to(acc);