]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide/src/hover.rs
move function to defs.rs
[rust.git] / crates / ide / src / hover.rs
index 506d3ba3c0dbd81a6c335d314ffa87e4a44b38a8..80e91f303cd1358019312731e16fd973a9702042 100644 (file)
@@ -4,10 +4,10 @@
 use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
 use ide_db::{
     base_db::{FileRange, SourceDatabase},
-    defs::{Definition, NameClass, NameRefClass},
+    defs::Definition,
     helpers::{
         generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
-        pick_best_token, try_resolve_derive_input_at, FamousDefs,
+        pick_best_token, FamousDefs,
     },
     RootDatabase,
 };
@@ -107,7 +107,7 @@ pub(crate) fn hover(
     }
     let offset = range.start();
 
-    let token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
+    let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
         IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
         T!['('] | T![')'] => 2,
         kind if kind.is_trivia() => 0,
@@ -117,11 +117,35 @@ pub(crate) fn hover(
     let mut seen = HashSet::default();
 
     let mut fallback = None;
-    sema.descend_into_macros_many(token.clone())
+    // attributes, require special machinery as they are mere ident tokens
+
+    let descend_macros = sema.descend_into_macros_many(original_token.clone());
+
+    for token in &descend_macros {
+        if token.kind() != COMMENT {
+            if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) {
+                // lints
+                if let Some(res) = try_hover_for_lint(&attr, &token) {
+                    return Some(res);
+                }
+            }
+        }
+    }
+
+    descend_macros
         .iter()
         .filter_map(|token| match token.parent() {
             Some(node) => {
-                match find_hover_result(&sema, file_id, offset, config, token, &node, &mut seen) {
+                match find_hover_result(
+                    &sema,
+                    file_id,
+                    offset,
+                    config,
+                    &original_token,
+                    token,
+                    &node,
+                    &mut seen,
+                ) {
                     Some(res) => match res {
                         ControlFlow::Break(inner) => Some(inner),
                         ControlFlow::Continue(_) => {
@@ -159,6 +183,7 @@ fn find_hover_result(
     file_id: FileId,
     offset: TextSize,
     config: &HoverConfig,
+    original_token: &SyntaxToken,
     token: &SyntaxToken,
     node: &SyntaxNode,
     seen: &mut HashSet<Definition>,
@@ -169,71 +194,39 @@ fn find_hover_result(
     // so don't add them to the `seen` duplicate check
     let mut add_to_seen_definitions = true;
 
-    let definition = match_ast! {
-        match node {
-            ast::Name(name) => NameClass::classify(sema, &name).map(|class| match class {
-                NameClass::Definition(it) | NameClass::ConstReference(it) => it,
-                NameClass::PatFieldShorthand { local_def, field_ref: _ } => Definition::Local(local_def),
-            }),
-            ast::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref).map(|class| match class {
-                NameRefClass::Definition(def) => def,
-                NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
-                    Definition::Field(field_ref)
-                }
-            }),
-            ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime).map_or_else(
-                || {
-                    NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class {
-                        NameRefClass::Definition(it) => Some(it),
-                        _ => None,
-                    })
+    let definition = Definition::from_node(sema, token).into_iter().next().or_else(|| {
+        // intra-doc links
+        // FIXME: move comment + attribute special cases somewhere else to simplify control flow,
+        // hopefully simplifying the return type of this function in the process
+        // (the `Break`/`Continue` distinction is needed to decide whether to use fallback hovers)
+        //
+        // FIXME: hovering the intra doc link to `Foo` not working:
+        //
+        // #[identity]
+        // trait Foo {
+        //    /// [`Foo`]
+        // fn foo() {}
+        if token.kind() == COMMENT {
+            add_to_seen_definitions = false;
+            cov_mark::hit!(no_highlight_on_comment_hover);
+            let (attributes, def) = doc_attributes(sema, node)?;
+            let (docs, doc_mapping) = attributes.docs_with_rangemap(sema.db)?;
+            let (idl_range, link, ns) = extract_definitions_from_docs(&docs).into_iter().find_map(
+                |(range, link, ns)| {
+                    let mapped = doc_mapping.map(range)?;
+                    (mapped.file_id == file_id.into() && mapped.value.contains(offset))
+                        .then(|| (mapped.value, link, ns))
                 },
-                NameClass::defined,
-            ),
-            _ => {
-                // intra-doc links
-                // FIXME: move comment + attribute special cases somewhere else to simplify control flow,
-                // hopefully simplifying the return type of this function in the process
-                // (the `Break`/`Continue` distinction is needed to decide whether to use fallback hovers)
-                //
-                // FIXME: hovering the intra doc link to `Foo` not working:
-                //
-                // #[identity]
-                // trait Foo {
-                //    /// [`Foo`]
-                // fn foo() {}
-                if token.kind() == COMMENT {
-                    add_to_seen_definitions = false;
-                    cov_mark::hit!(no_highlight_on_comment_hover);
-                    let (attributes, def) = doc_attributes(sema, node)?;
-                    let (docs, doc_mapping) = attributes.docs_with_rangemap(sema.db)?;
-                    let (idl_range, link, ns) =
-                        extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| {
-                            let mapped = doc_mapping.map(range)?;
-                            (mapped.file_id == file_id.into() && mapped.value.contains(offset)).then(||(mapped.value, link, ns))
-                        })?;
-                    range_override = Some(idl_range);
-                    Some(match resolve_doc_path_for_def(sema.db,def, &link,ns)? {
-                        Either::Left(it) => Definition::ModuleDef(it),
-                        Either::Right(it) => Definition::Macro(it),
-                    })
-                // attributes, require special machinery as they are mere ident tokens
-                } else if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) {
-                    add_to_seen_definitions = false;
-                    // lints
-                    if let Some(res) = try_hover_for_lint(&attr, &token) {
-                        return Some(ControlFlow::Break(res));
-                    // derives
-                    } else {
-                        range_override = Some(token.text_range());
-                        try_resolve_derive_input_at(&sema, &attr, &token).map(Definition::Macro)
-                    }
-                } else {
-                    None
-                }
-            },
+            )?;
+            range_override = Some(idl_range);
+            Some(match resolve_doc_path_for_def(sema.db, def, &link, ns)? {
+                Either::Left(it) => Definition::ModuleDef(it),
+                Either::Right(it) => Definition::Macro(it),
+            })
+        } else {
+            None
         }
-    };
+    });
 
     if let Some(definition) = definition {
         // skip duplicates
@@ -243,34 +236,8 @@ fn find_hover_result(
         if add_to_seen_definitions {
             seen.insert(definition);
         }
-        let famous_defs = match &definition {
-            Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => {
-                Some(FamousDefs(&sema, sema.scope(&node).krate()))
-            }
-            _ => None,
-        };
-        if let Some(markup) =
-            hover_for_definition(sema.db, definition, famous_defs.as_ref(), config)
-        {
-            let mut res = HoverResult::default();
-            res.markup = process_markup(sema.db, definition, &markup, config);
-            if let Some(action) = show_implementations_action(sema.db, definition) {
-                res.actions.push(action);
-            }
-
-            if let Some(action) = show_fn_references_action(sema.db, definition) {
-                res.actions.push(action);
-            }
-
-            if let Some(action) = runnable_action(&sema, definition, file_id) {
-                res.actions.push(action);
-            }
-
-            if let Some(action) = goto_type_action_for_def(sema.db, definition) {
-                res.actions.push(action);
-            }
-
-            let range = range_override.unwrap_or_else(|| sema.original_range(&node).range);
+        if let Some(res) = hover_for_definition(sema, file_id, definition, &node, config) {
+            let range = range_override.unwrap_or_else(|| original_token.text_range());
             return Some(ControlFlow::Break(RangeInfo::new(range, res)));
         }
     }
@@ -283,6 +250,9 @@ fn type_hover(
     config: &HoverConfig,
     token: &SyntaxToken,
 ) -> Option<RangeInfo<HoverResult>> {
+    if token.kind() == COMMENT {
+        return None;
+    }
     let node = token
         .ancestors()
         .take_while(|it| !ast::Item::can_cast(it.kind()))
@@ -749,7 +719,43 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
     def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
 }
 
-fn hover_for_definition(
+pub(crate) fn hover_for_definition(
+    sema: &Semantics<RootDatabase>,
+    file_id: FileId,
+    definition: Definition,
+    node: &SyntaxNode,
+    config: &HoverConfig,
+) -> Option<HoverResult> {
+    let famous_defs = match &definition {
+        Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => {
+            Some(FamousDefs(&sema, sema.scope(&node).krate()))
+        }
+        _ => None,
+    };
+    if let Some(markup) = markup_for_definition(sema.db, definition, famous_defs.as_ref(), config) {
+        let mut res = HoverResult::default();
+        res.markup = process_markup(sema.db, definition, &markup, config);
+        if let Some(action) = show_implementations_action(sema.db, definition) {
+            res.actions.push(action);
+        }
+
+        if let Some(action) = show_fn_references_action(sema.db, definition) {
+            res.actions.push(action);
+        }
+
+        if let Some(action) = runnable_action(&sema, definition, file_id) {
+            res.actions.push(action);
+        }
+
+        if let Some(action) = goto_type_action_for_def(sema.db, definition) {
+            res.actions.push(action);
+        }
+        return Some(res);
+    }
+    None
+}
+
+fn markup_for_definition(
     db: &RootDatabase,
     def: Definition,
     famous_defs: Option<&FamousDefs>,
@@ -885,7 +891,7 @@ fn check_hover_no_result(ra_fixture: &str) {
                 FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
             )
             .unwrap();
-        assert!(hover.is_none());
+        assert!(hover.is_none(), "hover not expected but found: {:?}", hover.unwrap());
     }
 
     fn check(ra_fixture: &str, expect: Expect) {
@@ -4529,6 +4535,36 @@ mod bar {
         );
     }
 
+    #[test]
+    fn hover_attribute_in_macro() {
+        check(
+            r#"
+macro_rules! identity {
+    ($struct:item) => {
+        $struct
+    };
+}
+#[rustc_builtin_macro]
+pub macro Copy {}
+identity!{
+    #[derive(Copy$0)]
+    struct Foo;
+}
+"#,
+            expect![[r#"
+                *Copy*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub macro Copy
+                ```
+            "#]],
+        );
+    }
+
     #[test]
     fn hover_derive_input() {
         check(