about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2022-02-02 13:35:46 +0100
committerLukas Wirth <lukastw97@gmail.com>2022-02-03 15:50:14 +0100
commit6940cca76065f7871e53ef2a95019e0841ff408d (patch)
tree8a038710e26724d3eb0ee5a94c70eda48abdc461
parent46b5089bfaa9a6b1e72a96c922fa3dec736aeaf5 (diff)
downloadrust-6940cca76065f7871e53ef2a95019e0841ff408d.tar.gz
rust-6940cca76065f7871e53ef2a95019e0841ff408d.zip
Move attribute path completions into attribute completion module
-rw-r--r--crates/hir/src/semantics.rs15
-rw-r--r--crates/hir_def/src/path.rs4
-rw-r--r--crates/hir_ty/src/infer.rs1
-rw-r--r--crates/ide_completion/src/completions/attribute.rs109
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs4
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs37
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs11
-rw-r--r--crates/ide_completion/src/context.rs51
-rw-r--r--crates/ide_completion/src/lib.rs1
-rw-r--r--crates/ide_completion/src/tests/attribute.rs11
-rw-r--r--crates/syntax/src/ast/node_ext.rs2
11 files changed, 139 insertions, 107 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 9ac88e260c4..243ba63b8a0 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -949,12 +949,15 @@ impl<'db> SemanticsImpl<'db> {
         })?;
 
         match res {
-            Either::Left(path) => resolve_hir_path(
-                self.db,
-                &self.scope(derive.syntax()).resolver,
-                &Path::from_known_path(path, []),
-            )
-            .filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))),
+            Either::Left(path) => {
+                let len = path.len();
+                resolve_hir_path(
+                    self.db,
+                    &self.scope(derive.syntax()).resolver,
+                    &Path::from_known_path(path, vec![None; len]),
+                )
+                .filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_))))
+            }
             Either::Right(derive) => derive
                 .map(|call| MacroDef { id: self.db.lookup_intern_macro_call(call).def })
                 .map(PathResolution::Macro),
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index fc81b88db3f..a6141174c83 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -92,7 +92,9 @@ impl Path {
         path: ModPath,
         generic_args: impl Into<Box<[Option<Interned<GenericArgs>>]>>,
     ) -> Path {
-        Path { type_anchor: None, mod_path: Interned::new(path), generic_args: generic_args.into() }
+        let generic_args = generic_args.into();
+        assert_eq!(path.len(), generic_args.len());
+        Path { type_anchor: None, mod_path: Interned::new(path), generic_args }
     }
 
     pub fn kind(&self) -> &PathKind {
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index a6c91f9d27e..173380654e0 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -253,6 +253,7 @@ pub enum PointerCast {
     /// Go from a mut raw pointer to a const raw pointer.
     MutToConstPointer,
 
+    #[allow(dead_code)]
     /// Go from `*const [T; N]` to `*const T`
     ArrayToPointer,
 
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index 6b6759ebfd9..8950b13d6f0 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -16,62 +16,95 @@ use ide_db::{
 use itertools::Itertools;
 use once_cell::sync::Lazy;
 use rustc_hash::FxHashMap;
-use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, SyntaxKind, T};
+use syntax::{
+    ast::{self, AttrKind},
+    AstNode, SyntaxKind, T,
+};
 
-use crate::{context::CompletionContext, item::CompletionItem, Completions};
+use crate::{
+    completions::module_or_attr,
+    context::{CompletionContext, PathCompletionContext, PathKind},
+    item::CompletionItem,
+    Completions,
+};
 
 mod cfg;
 mod derive;
 mod lint;
 mod repr;
 
-pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
+/// Complete inputs to known builtin attributes as well as derive attributes
+pub(crate) fn complete_known_attribute_input(
+    acc: &mut Completions,
+    ctx: &CompletionContext,
+) -> Option<()> {
     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,
     };
-    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, 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)?;
+    let (path, tt) = name_ref.zip(attribute.token_tree())?;
+    if tt.l_paren_token().is_none() {
+        return None;
+    }
 
-                let lints: Vec<Lint> = CLIPPY_LINT_GROUPS
-                    .iter()
-                    .map(|g| &g.lint)
-                    .chain(DEFAULT_LINTS.iter())
-                    .chain(CLIPPY_LINTS.iter())
-                    .chain(RUSTDOC_LINTS)
-                    .cloned()
-                    .collect();
+    match path.text().as_str() {
+        "repr" => repr::complete_repr(acc, ctx, 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)?;
 
-                lint::complete_lint(acc, ctx, &existing_lints, &lints);
-            }
-            "cfg" => {
-                cfg::complete_cfg(acc, ctx);
-            }
-            _ => (),
-        },
-        (_, Some(_)) => (),
-        (_, None) if attribute.expr().is_some() => (),
-        (_, None) => complete_new_attribute(acc, ctx, attribute),
+            let lints: Vec<Lint> = CLIPPY_LINT_GROUPS
+                .iter()
+                .map(|g| &g.lint)
+                .chain(DEFAULT_LINTS)
+                .chain(CLIPPY_LINTS)
+                .chain(RUSTDOC_LINTS)
+                .cloned()
+                .collect();
+
+            lint::complete_lint(acc, ctx, &existing_lints, &lints);
+        }
+        "cfg" => {
+            cfg::complete_cfg(acc, ctx);
+        }
+        _ => (),
     }
     Some(())
 }
 
-// FIXME?: Move this functionality to (un)qualified_path, make this module work solely for builtin/known attributes for their inputs?
-fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
-    let is_inner = attribute.kind() == ast::AttrKind::Inner;
-    let attribute_annotated_item_kind =
-        attribute.syntax().parent().map(|it| it.kind()).filter(|_| {
-            is_inner
-            // If we got nothing coming after the attribute it could be anything so filter it the kind out
-                || non_trivia_sibling(attribute.syntax().clone().into(), Direction::Next).is_some()
-        });
-    let attributes = attribute_annotated_item_kind.and_then(|kind| {
+pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
+    let (is_trivial_path, qualifier, is_inner, annotated_item_kind) = match ctx.path_context {
+        Some(PathCompletionContext {
+            kind: Some(PathKind::Attr { kind, annotated_item_kind }),
+            is_trivial_path,
+            ref qualifier,
+            ..
+        }) => (is_trivial_path, qualifier, kind == AttrKind::Inner, annotated_item_kind),
+        _ => return,
+    };
+
+    if !is_trivial_path {
+        return;
+    }
+
+    if let Some((_, Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))))) = qualifier {
+        for (name, def) in module.scope(ctx.db, ctx.module) {
+            if let Some(def) = module_or_attr(def) {
+                acc.add_resolution(ctx, name, def);
+            }
+        }
+        return;
+    }
+
+    ctx.process_all_names(&mut |name, def| {
+        if let Some(def) = module_or_attr(def) {
+            acc.add_resolution(ctx, name, def);
+        }
+    });
+
+    let attributes = annotated_item_kind.and_then(|kind| {
         if ast::Expr::can_cast(kind) {
             Some(EXPR_ATTRIBUTES)
         } else {
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 8ca4634be25..723abd62aef 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -171,8 +171,8 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
             (PathKind::Type, ItemInNs::Types(_)) => true,
             (PathKind::Type, ItemInNs::Values(_)) => false,
 
-            (PathKind::Attr, ItemInNs::Macros(mac)) => mac.is_attr(),
-            (PathKind::Attr, _) => false,
+            (PathKind::Attr { .. }, ItemInNs::Macros(mac)) => mac.is_attr(),
+            (PathKind::Attr { .. }, _) => false,
         }
     };
 
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 2c6899ff0cd..b15ec38b0ad 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -7,7 +7,7 @@ use rustc_hash::FxHashSet;
 use syntax::{ast, AstNode};
 
 use crate::{
-    completions::{module_or_attr, module_or_fn_macro},
+    completions::module_or_fn_macro,
     context::{PathCompletionContext, PathKind},
     patterns::ImmediateLocation,
     CompletionContext, Completions,
@@ -17,7 +17,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
     if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
         return;
     }
-    let (path, use_tree_parent, kind) = match ctx.path_context {
+    let ((path, resolution), use_tree_parent, kind) = match ctx.path_context {
         // let ... else, syntax would come in really handy here right now
         Some(PathCompletionContext {
             qualifier: Some(ref qualifier),
@@ -47,17 +47,15 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
         }
     }
 
-    let resolution = match ctx.sema.resolve_path(path) {
+    let resolution = match resolution {
         Some(res) => res,
         None => return,
     };
 
-    let context_module = ctx.module;
-
     match ctx.completion_location {
         Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
             if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
-                for (name, def) in module.scope(ctx.db, context_module) {
+                for (name, def) in module.scope(ctx.db, ctx.module) {
                     if let Some(def) = module_or_fn_macro(def) {
                         acc.add_resolution(ctx, name, def);
                     }
@@ -76,7 +74,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                     let next_towards_current = current_module
                         .path_to_root(ctx.db)
                         .into_iter()
-                        .take_while(|&it| it != module)
+                        .take_while(|it| it != module)
                         .next();
                     if let Some(next) = next_towards_current {
                         if let Some(name) = next.name(ctx.db) {
@@ -88,14 +86,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
             }
             return;
         }
-        Some(PathKind::Attr) => {
-            if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
-                for (name, def) in module.scope(ctx.db, context_module) {
-                    if let Some(def) = module_or_attr(def) {
-                        acc.add_resolution(ctx, name, def);
-                    }
-                }
-            }
+        Some(PathKind::Attr { .. }) => {
             return;
         }
         Some(PathKind::Use) => {
@@ -127,7 +118,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
 
     match resolution {
         hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
-            let module_scope = module.scope(ctx.db, context_module);
+            let module_scope = module.scope(ctx.db, ctx.module);
             for (name, def) in module_scope {
                 if let Some(PathKind::Use) = kind {
                     if let ScopeDef::Unknown = def {
@@ -168,7 +159,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
             | hir::ModuleDef::TypeAlias(_)
             | hir::ModuleDef::BuiltinType(_)),
         ) => {
-            if let hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def {
+            if let &hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def {
                 add_enum_variants(acc, ctx, e);
             }
             let ty = match def {
@@ -623,18 +614,6 @@ fn foo() {
     }
 
     #[test]
-    fn dont_complete_attr() {
-        check(
-            r#"
-mod foo { pub struct Foo; }
-#[foo::$0]
-fn f() {}
-"#,
-            expect![[""]],
-        );
-    }
-
-    #[test]
     fn completes_variant_through_self() {
         check(
             r#"
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 7e06b074ce2..01f3f777a1a 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -4,7 +4,7 @@ use hir::ScopeDef;
 use syntax::{ast, AstNode};
 
 use crate::{
-    completions::{module_or_attr, module_or_fn_macro},
+    completions::module_or_fn_macro,
     context::{PathCompletionContext, PathKind},
     patterns::ImmediateLocation,
     CompletionContext, Completions,
@@ -36,14 +36,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
 
     match kind {
         Some(PathKind::Vis { .. }) => return,
-        Some(PathKind::Attr) => {
-            ctx.process_all_names(&mut |name, def| {
-                if let Some(def) = module_or_attr(def) {
-                    acc.add_resolution(ctx, name, def);
-                }
-            });
-            return;
-        }
+        Some(PathKind::Attr { .. }) => return,
         _ => (),
     }
 
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index ab55d9cc04a..4845427422c 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -3,7 +3,9 @@
 use std::iter;
 
 use base_db::SourceDatabaseExt;
-use hir::{HasAttrs, Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo};
+use hir::{
+    HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
+};
 use ide_db::{
     active_parameter::ActiveParameter,
     base_db::{FilePosition, SourceDatabase},
@@ -11,8 +13,8 @@ use ide_db::{
     RootDatabase,
 };
 use syntax::{
-    algo::find_node_at_offset,
-    ast::{self, HasName, NameOrNameRef},
+    algo::{find_node_at_offset, non_trivia_sibling},
+    ast::{self, AttrKind, HasName, NameOrNameRef},
     match_ast, AstNode, NodeOrToken,
     SyntaxKind::{self, *},
     SyntaxNode, SyntaxToken, TextRange, TextSize, T,
@@ -44,7 +46,7 @@ pub(crate) enum Visible {
 pub(super) enum PathKind {
     Expr,
     Type,
-    Attr,
+    Attr { kind: AttrKind, annotated_item_kind: Option<SyntaxKind> },
     Mac,
     Pat,
     Vis { has_in_token: bool },
@@ -58,7 +60,7 @@ pub(crate) struct PathCompletionContext {
     /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
     pub(super) is_trivial_path: bool,
     /// If not a trivial path, the prefix (qualifier).
-    pub(super) qualifier: Option<ast::Path>,
+    pub(super) qualifier: Option<(ast::Path, Option<PathResolution>)>,
     #[allow(dead_code)]
     /// If not a trivial path, the suffix (parent).
     pub(super) parent: Option<ast::Path>,
@@ -282,7 +284,7 @@ impl<'a> CompletionContext<'a> {
     }
 
     pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
-        self.path_context.as_ref().and_then(|it| it.qualifier.as_ref())
+        self.path_context.as_ref().and_then(|it| it.qualifier.as_ref().map(|(it, _)| it))
     }
 
     pub(crate) fn path_kind(&self) -> Option<PathKind> {
@@ -786,7 +788,7 @@ impl<'a> CompletionContext<'a> {
     }
 
     fn classify_name_ref(
-        _sema: &Semantics<RootDatabase>,
+        sema: &Semantics<RootDatabase>,
         original_file: &SyntaxNode,
         name_ref: ast::NameRef,
     ) -> Option<(PathCompletionContext, Option<PatternContext>)> {
@@ -808,8 +810,9 @@ impl<'a> CompletionContext<'a> {
         let mut pat_ctx = None;
         path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax());
 
-        path_ctx.kind  = path.syntax().ancestors().find_map(|it| {
-            match_ast! {
+        path_ctx.kind = path.syntax().ancestors().find_map(|it| {
+            // using Option<Option<PathKind>> as extra controlflow
+            let kind = match_ast! {
                 match it {
                     ast::PathType(_) => Some(PathKind::Type),
                     ast::PathExpr(it) => {
@@ -830,21 +833,41 @@ impl<'a> CompletionContext<'a> {
                         Some(PathKind::Pat)
                     },
                     ast::MacroCall(it) => it.excl_token().and(Some(PathKind::Mac)),
-                    ast::Meta(_) => Some(PathKind::Attr),
+                    ast::Meta(meta) => (|| {
+                        let attr = meta.parent_attr()?;
+                        let kind = attr.kind();
+                        let attached = attr.syntax().parent()?;
+                        let is_trailing_outer_attr = kind != AttrKind::Inner
+                            && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
+                        let annotated_item_kind = if is_trailing_outer_attr {
+                            None
+                        } else {
+                            Some(attached.kind())
+                        };
+                        Some(PathKind::Attr {
+                            kind,
+                            annotated_item_kind,
+                        })
+                    })(),
                     ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
                     ast::UseTree(_) => Some(PathKind::Use),
-                    _ => None,
+                    _ => return None,
                 }
-            }
-        });
+            };
+            Some(kind)
+        }).flatten();
         path_ctx.has_type_args = segment.generic_arg_list().is_some();
 
         if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
             path_ctx.use_tree_parent = use_tree_parent;
-            path_ctx.qualifier = path
+            let path = path
                 .segment()
                 .and_then(|it| find_node_in_file(original_file, &it))
                 .map(|it| it.parent_path());
+            path_ctx.qualifier = path.map(|path| {
+                let res = sema.resolve_path(&path);
+                (path, res)
+            });
             return Some((path_ctx, pat_ctx));
         }
 
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index a2217af493d..5b49a499119 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -151,6 +151,7 @@ pub fn completions(
     }
 
     let mut acc = Completions::default();
+    completions::attribute::complete_known_attribute_input(&mut acc, &ctx);
     completions::attribute::complete_attribute(&mut acc, &ctx);
     completions::fn_param::complete_fn_param(&mut acc, &ctx);
     completions::keyword::complete_expr_keyword(&mut acc, &ctx);
diff --git a/crates/ide_completion/src/tests/attribute.rs b/crates/ide_completion/src/tests/attribute.rs
index 2d2a1b867a6..ac43a67bdcf 100644
--- a/crates/ide_completion/src/tests/attribute.rs
+++ b/crates/ide_completion/src/tests/attribute.rs
@@ -17,6 +17,7 @@ fn proc_macros() {
 struct Foo;
 "#,
         expect![[r#"
+            md proc_macros
             at allow(…)
             at cfg(…)
             at cfg_attr(…)
@@ -35,7 +36,6 @@ struct Foo;
             kw self
             kw super
             kw crate
-            md proc_macros
         "#]],
     )
 }
@@ -61,10 +61,7 @@ fn proc_macros_qualified() {
 #[proc_macros::$0]
 struct Foo;
 "#,
-        expect![[r#"
-            at input_replace pub macro input_replace
-            at identity      pub macro identity
-        "#]],
+        expect![[r#""#]],
     )
 }
 
@@ -302,6 +299,8 @@ fn attr_on_struct() {
 struct Foo;
 "#,
         expect![[r#"
+            md core
+            at derive           pub macro derive
             at allow(…)
             at cfg(…)
             at cfg_attr(…)
@@ -320,8 +319,6 @@ struct Foo;
             kw self
             kw super
             kw crate
-            md core
-            at derive           pub macro derive
         "#]],
     );
 }
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 7211c77e880..067e13ee14d 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -119,7 +119,7 @@ impl From<ast::AssocItem> for ast::Item {
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum AttrKind {
     Inner,
     Outer,