about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/goto_definition.rs79
-rw-r--r--crates/ide/src/hover.rs57
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs14
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html2
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs28
-rw-r--r--crates/ide_db/src/helpers.rs34
6 files changed, 164 insertions, 50 deletions
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 4a029072741..ef4fa968007 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -5,7 +5,7 @@ use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
 use ide_db::{
     base_db::{AnchoredPath, FileId, FileLoader},
     defs::{Definition, NameClass, NameRefClass},
-    helpers::pick_best_token,
+    helpers::{pick_best_token, try_resolve_derive_input_at},
     RootDatabase,
 };
 use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
@@ -78,7 +78,7 @@ pub(crate) fn goto_definition(
             } else {
                 reference_definition(&sema, Either::Left(&lt))
             },
-            ast::TokenTree(tt) => try_lookup_include_path(sema.db, tt, token, position.file_id)?,
+            ast::TokenTree(tt) => try_lookup_include_path_or_derive(&sema, tt, token, position.file_id)?,
             _ => return None,
         }
     };
@@ -86,30 +86,41 @@ pub(crate) fn goto_definition(
     Some(RangeInfo::new(original_token.text_range(), navs))
 }
 
-fn try_lookup_include_path(
-    db: &RootDatabase,
+fn try_lookup_include_path_or_derive(
+    sema: &Semantics<RootDatabase>,
     tt: ast::TokenTree,
     token: SyntaxToken,
     file_id: FileId,
 ) -> Option<Vec<NavigationTarget>> {
-    let path = ast::String::cast(token)?.value()?.into_owned();
-    let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
-    let name = macro_call.path()?.segment()?.name_ref()?;
-    if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
-        return None;
-    }
-    let file_id = db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
-    let size = db.file_text(file_id).len().try_into().ok()?;
-    Some(vec![NavigationTarget {
-        file_id,
-        full_range: TextRange::new(0.into(), size),
-        name: path.into(),
-        focus_range: None,
-        kind: None,
-        container_name: None,
-        description: None,
-        docs: None,
-    }])
+    match ast::String::cast(token.clone()) {
+        Some(token) => {
+            let path = token.value()?.into_owned();
+            let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
+            let name = macro_call.path()?.segment()?.name_ref()?;
+            if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
+                return None;
+            }
+            let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
+            let size = sema.db.file_text(file_id).len().try_into().ok()?;
+            Some(vec![NavigationTarget {
+                file_id,
+                full_range: TextRange::new(0.into(), size),
+                name: path.into(),
+                focus_range: None,
+                kind: None,
+                container_name: None,
+                description: None,
+                docs: None,
+            }])
+        }
+        None => try_resolve_derive_input_at(
+            sema,
+            &tt.syntax().ancestors().nth(2).and_then(ast::Attr::cast)?,
+            &token,
+        )
+        .and_then(|it| it.try_to_nav(sema.db))
+        .map(|it| vec![it]),
+    }
 }
 
 /// finds the trait definition of an impl'd item
@@ -1383,4 +1394,28 @@ impl Twait for Stwuct {
 "#,
         );
     }
+
+    #[test]
+    fn goto_def_derive_input() {
+        check(
+            r#"
+#[rustc_builtin_macro]
+pub macro Copy {}
+       // ^^^^
+#[derive(Copy$0)]
+struct Foo;
+            "#,
+        );
+        check(
+            r#"
+mod foo {
+    #[rustc_builtin_macro]
+    pub macro Copy {}
+           // ^^^^
+}
+#[derive(foo::Copy$0)]
+struct Foo;
+            "#,
+        );
+    }
 }
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index d0201d315db..5a23e05ae88 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -5,7 +5,7 @@ use ide_db::{
     defs::{Definition, NameClass, NameRefClass},
     helpers::{
         generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
-        pick_best_token, FamousDefs,
+        pick_best_token, try_resolve_derive_input_at, FamousDefs,
     },
     RootDatabase,
 };
@@ -129,8 +129,12 @@ pub(crate) fn hover(
                         })?;
                     range = Some(idl_range);
                     resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef)
-                } else if let res@Some(_) = try_hover_for_attribute(&token) {
-                    return res;
+                } else if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) {
+                    if let res@Some(_) = try_hover_for_lint(&attr, &token) {
+                        return res;
+                    } else {
+                        try_resolve_derive_input_at(&sema, &attr, &token).map(Definition::Macro)
+                    }
                 } else {
                     None
                 }
@@ -197,8 +201,7 @@ pub(crate) fn hover(
     Some(RangeInfo::new(range, res))
 }
 
-fn try_hover_for_attribute(token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> {
-    let attr = token.ancestors().find_map(ast::Attr::cast)?;
+fn try_hover_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> {
     let (path, tt) = attr.as_simple_call()?;
     if !tt.syntax().text_range().contains(token.text_range().start()) {
         return None;
@@ -3839,4 +3842,48 @@ use crate as foo$0;
             "#]],
         );
     }
+
+    #[test]
+    fn hover_derive_input() {
+        check(
+            r#"
+#[rustc_builtin_macro]
+pub macro Copy {}
+#[derive(Copy$0)]
+struct Foo;
+            "#,
+            expect![[r#"
+                *(Copy)*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub macro Copy
+                ```
+            "#]],
+        );
+        check(
+            r#"
+mod foo {
+    #[rustc_builtin_macro]
+    pub macro Copy {}
+}
+#[derive(foo::Copy$0)]
+struct Foo;
+            "#,
+            expect![[r#"
+                *(foo::Copy)*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                pub macro Copy
+                ```
+            "#]],
+        );
+    }
 }
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index a8e93c39f89..6ace8ce83c6 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -3,6 +3,7 @@
 use hir::{AsAssocItem, HasVisibility, Semantics};
 use ide_db::{
     defs::{Definition, NameClass, NameRefClass},
+    helpers::try_resolve_derive_input_at,
     RootDatabase, SymbolKind,
 };
 use rustc_hash::FxHashMap;
@@ -87,7 +88,18 @@ pub(super) fn element(
                 _ => Highlight::from(SymbolKind::LifetimeParam) | HlMod::Definition,
             }
         }
-        IDENT if parent_matches::<ast::TokenTree>(&element) => HlTag::None.into(),
+        IDENT if parent_matches::<ast::TokenTree>(&element) => {
+            if let Some((attr, token)) =
+                element.ancestors().nth(2).and_then(ast::Attr::cast).zip(element.as_token())
+            {
+                match try_resolve_derive_input_at(sema, &attr, token) {
+                    Some(makro) => highlight_def(sema.db, krate, Definition::Macro(makro)),
+                    None => HlTag::None.into(),
+                }
+            } else {
+                HlTag::None.into()
+            }
+        }
         p if p.is_punct() => match p {
             T![&] if parent_matches::<ast::BinExpr>(&element) => HlOperator::Bitwise.into(),
             T![&] => {
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 04148767d0c..42416afa251 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -91,7 +91,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">derive</span><span class="parenthesis attribute">(</span><span class="none attribute">Copy</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">derive</span><span class="parenthesis attribute">(</span><span class="macro attribute">Copy</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
 <span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="brace">{</span>
     <span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
 <span class="brace">}</span>
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index c4bd5eaa7b7..2cee442bac9 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -43,8 +43,8 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
     let expr = match_expr.expr()?;
 
     let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
-    if arms.len() == 1 {
-        if let Some(Pat::WildcardPat(..)) = arms[0].pat() {
+    if let [arm] = arms.as_slice() {
+        if let Some(Pat::WildcardPat(..)) = arm.pat() {
             arms.clear();
         }
     }
@@ -73,9 +73,9 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
             .filter_map(|variant| build_pat(ctx.db(), module, variant))
             .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat));
 
-        let missing_pats: Box<dyn Iterator<Item = _>> = if Some(enum_def)
-            == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option().map(lift_enum)
-        {
+        let option_enum =
+            FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option().map(lift_enum);
+        let missing_pats: Box<dyn Iterator<Item = _>> = if Some(enum_def) == option_enum {
             // Match `Some` variant first.
             cov_mark::hit!(option_order);
             Box::new(missing_pats.rev())
@@ -136,7 +136,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
                 .arms()
                 .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
             if let Some(arm) = catch_all_arm {
-                arm.remove()
+                arm.remove();
             }
             let mut first_new_arm = None;
             for arm in missing_arms {
@@ -214,13 +214,7 @@ impl ExtendedEnum {
 fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
     sema.type_of_expr(expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
         Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
-        _ => {
-            if ty.is_bool() {
-                Some(ExtendedEnum::Bool)
-            } else {
-                None
-            }
-        }
+        _ => ty.is_bool().then(|| ExtendedEnum::Bool),
     })
 }
 
@@ -237,13 +231,7 @@ fn resolve_tuple_of_enum_def(
                 // For now we only handle expansion for a tuple of enums. Here
                 // we map non-enum items to None and rely on `collect` to
                 // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
-                _ => {
-                    if ty.is_bool() {
-                        Some(ExtendedEnum::Bool)
-                    } else {
-                        None
-                    }
-                }
+                _ => ty.is_bool().then(|| ExtendedEnum::Bool),
             })
         })
         .collect()
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index c71b6d64798..f9da39f833d 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -12,7 +12,7 @@ use either::Either;
 use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
 use syntax::{
     ast::{self, make, LoopBodyOwner},
-    AstNode, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent,
+    AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent, T,
 };
 
 use crate::RootDatabase;
@@ -25,6 +25,38 @@ pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
     }
 }
 
+/// Resolves the path at the cursor token as a derive macro if it inside a token tree of a derive attribute.
+pub fn try_resolve_derive_input_at(
+    sema: &Semantics<RootDatabase>,
+    derive_attr: &ast::Attr,
+    cursor: &SyntaxToken,
+) -> Option<MacroDef> {
+    use itertools::Itertools;
+    if cursor.kind() != T![ident] {
+        return None;
+    }
+    let tt = match derive_attr.as_simple_call() {
+        Some((name, tt))
+            if name == "derive" && tt.syntax().text_range().contains_range(cursor.text_range()) =>
+        {
+            tt
+        }
+        _ => return None,
+    };
+    let tokens: Vec<_> = cursor
+        .siblings_with_tokens(Direction::Prev)
+        .flat_map(SyntaxElement::into_token)
+        .take_while(|tok| tok.kind() != T!['('] && tok.kind() != T![,])
+        .collect();
+    let path = ast::Path::parse(&tokens.into_iter().rev().join("")).ok()?;
+    match sema.scope(tt.syntax()).speculative_resolve(&path) {
+        Some(hir::PathResolution::Macro(makro)) if makro.kind() == hir::MacroKind::Derive => {
+            Some(makro)
+        }
+        _ => None,
+    }
+}
+
 /// Picks the token with the highest rank returned by the passed in function.
 pub fn pick_best_token(
     tokens: TokenAtOffset<SyntaxToken>,