From 6b7b09d329c174590a52ba570e20def528ea216a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 1 Jan 2022 20:31:04 +0100 Subject: [PATCH 1/4] internal: Record unresolved derive invocations in hir --- crates/hir/src/semantics.rs | 77 ++++++++++++++++--- crates/hir_def/src/attr.rs | 7 +- crates/hir_def/src/nameres/collector.rs | 2 +- crates/ide/src/goto_definition.rs | 12 +++ .../ide/src/syntax_highlighting/highlight.rs | 11 +-- crates/ide_db/src/defs.rs | 9 +-- crates/ide_db/src/helpers.rs | 20 ----- crates/ide_db/src/helpers/import_assets.rs | 1 + 8 files changed, 87 insertions(+), 52 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 645b10f8534e..077ab40d0815 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -18,13 +18,14 @@ use smallvec::{smallvec, SmallVec}; use syntax::{ algo::skip_trivia_token, ast::{self, HasAttrs, HasGenericParams, HasLoopBody}, - match_ast, AstNode, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize, + match_ast, AstNode, AstToken, Direction, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, + TextSize, T, }; use crate::{ db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, - source_analyzer::{resolve_hir_path, resolve_hir_path_as_macro, SourceAnalyzer}, + source_analyzer::{resolve_hir_path, SourceAnalyzer}, Access, AssocItem, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef, @@ -354,6 +355,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_bind_pat_to_const(pat) } + pub fn resolve_derive_ident(&self, ident: &ast::Ident) -> Option { + self.imp.resolve_derive_ident(ident) + } + // FIXME: use this instead? // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option; @@ -894,6 +899,64 @@ impl<'db> SemanticsImpl<'db> { self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat) } + fn resolve_derive_ident(&self, ident: &ast::Ident) -> Option { + // derive macros are always at depth 2, tokentree -> meta -> attribute + let syntax = ident.syntax(); + let attr = syntax.ancestors().nth(2).and_then(ast::Attr::cast)?; + + let tt = attr.token_tree()?; + if !tt.syntax().text_range().contains_range(ident.syntax().text_range()) { + return None; + } + + // Fetch hir::Attr definition + // FIXME: Move this to ToDef impl? + let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; + let attr_pos = adt.attrs().position(|it| it == attr)?; + let attrs = { + let file_id = self.find_file(adt.syntax()).file_id; + let adt = InFile::new(file_id, adt); + let def = self.with_ctx(|ctx| ctx.adt_to_def(adt))?; + self.db.attrs(def.into()) + }; + let attr_def = attrs.get(attr_pos)?; + + let mut derive_paths = attr_def.parse_path_comma_token_tree()?; + let derives = self.resolve_derive_macro(&attr)?; + + let derive_idx = tt + .syntax() + .children_with_tokens() + .filter_map(SyntaxElement::into_token) + .take_while(|tok| tok != syntax) + .filter(|t| t.kind() == T![,]) + .count(); + let path_segment_idx = syntax + .siblings_with_tokens(Direction::Prev) + .filter_map(SyntaxElement::into_token) + .take_while(|tok| matches!(tok.kind(), T![:] | T![ident])) + .filter(|tok| tok.kind() == T![ident]) + .count(); + + let mut mod_path = derive_paths.nth(derive_idx)?; + + if path_segment_idx < mod_path.len() { + // the path for the given ident is a qualifier, resolve to module if possible + while path_segment_idx < mod_path.len() { + mod_path.pop_segment(); + } + resolve_hir_path( + self.db, + &self.scope(attr.syntax()).resolver, + &Path::from_known_path(mod_path, []), + ) + .filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))) + } else { + // otherwise fetch the derive + derives.get(derive_idx)?.map(PathResolution::Macro) + } + } + fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> { self.analyze(literal.syntax()) .record_literal_missing_fields(self.db, literal) @@ -1230,14 +1293,4 @@ impl<'a> SemanticsScope<'a> { let path = Path::from_src(path.clone(), &ctx)?; resolve_hir_path(self.db, &self.resolver, &path) } - - /// Resolve a path as-if it was written at the given scope. This is - /// necessary a heuristic, as it doesn't take hygiene into account. - // FIXME: This special casing solely exists for attributes for now - // ideally we should have a path resolution infra that properly knows about overlapping namespaces - pub fn speculative_resolve_as_mac(&self, path: &ast::Path) -> Option { - let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id); - let path = Path::from_src(path.clone(), &ctx)?; - resolve_hir_path_as_macro(self.db, &self.resolver, &path) - } } diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 383ad7f0c832..c63cd4c195bf 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs @@ -720,11 +720,8 @@ impl Attr { Self::from_src(db, ast, hygiene, id) } - /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths - /// to derive macros. - /// - /// Returns `None` when the attribute does not have a well-formed `#[derive]` attribute input. - pub(crate) fn parse_derive(&self) -> Option + '_> { + /// Parses this attribute as a token tree consisting of comma separated paths. + pub fn parse_path_comma_token_tree(&self) -> Option + '_> { let args = match self.input.as_deref() { Some(AttrInput::TokenTree(args, _)) => args, _ => return None, diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index ca8afe8cbfd7..be749c3b46f4 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -1145,7 +1145,7 @@ impl DefCollector<'_> { } } - match attr.parse_derive() { + match attr.parse_path_comma_token_tree() { Some(derive_macros) => { let mut len = 0; for (idx, path) in derive_macros.enumerate() { diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 0e71d5ff6acb..4d638e687ae3 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1381,6 +1381,18 @@ mod foo { // ^^^^ } #[derive(foo::Copy$0)] +struct Foo; + "#, + ); + check( + r#" +//- minicore:derive +mod foo { + // ^^^ + #[rustc_builtin_macro] + pub macro Copy {} +} +#[derive(foo$0::Copy)] struct Foo; "#, ); diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 4a09d341f709..f82ccaa1c764 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -3,7 +3,7 @@ use hir::{AsAssocItem, HasVisibility, Semantics}; use ide_db::{ defs::{Definition, NameClass, NameRefClass}, - helpers::{try_resolve_derive_input, FamousDefs}, + helpers::FamousDefs, RootDatabase, SymbolKind, }; use rustc_hash::FxHashMap; @@ -40,13 +40,8 @@ pub(super) fn token( BYTE => HlTag::ByteLiteral.into(), CHAR => HlTag::CharLiteral.into(), IDENT if parent_matches::(&token) => { - match token.ancestors().nth(2).and_then(ast::Attr::cast) { - Some(attr) => { - match try_resolve_derive_input(sema, &attr, &ast::Ident::cast(token).unwrap()) { - Some(res) => highlight_def(sema, krate, Definition::from(res)), - None => HlTag::None.into(), - } - } + match sema.resolve_derive_ident(&ast::Ident::cast(token).unwrap()) { + Some(res) => highlight_def(sema, krate, Definition::from(res)), None => HlTag::None.into(), } } diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index c9b8c942051a..d69fd9bd2170 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs @@ -17,7 +17,7 @@ use syntax::{ match_ast, AstToken, SyntaxKind, SyntaxNode, SyntaxToken, }; -use crate::{helpers::try_resolve_derive_input, RootDatabase}; +use crate::RootDatabase; // FIXME: a more precise name would probably be `Symbol`? #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] @@ -55,11 +55,8 @@ impl Definition { let attr = ast::TokenTree::cast(parent.clone()) .and_then(|tt| tt.parent_meta()) .and_then(|meta| meta.parent_attr()); - if let Some(attr) = attr { - return try_resolve_derive_input(&sema, &attr, &ident) - .map(Into::into) - .into_iter() - .collect(); + if let Some(_) = attr { + return sema.resolve_derive_ident(&ident).map(Into::into).into_iter().collect(); } } Self::from_node(sema, &parent) diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index e589940dae29..344f8db8d00a 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs @@ -74,26 +74,6 @@ pub fn get_path_at_cursor_in_tt(cursor: &ast::Ident) -> Option { }) } -/// Parses and resolves the path at the cursor position in the given attribute, if it is a derive. -/// This special case is required because the derive macro is a compiler builtin that discards the input derives. -pub fn try_resolve_derive_input( - sema: &hir::Semantics, - attr: &ast::Attr, - cursor: &ast::Ident, -) -> Option { - let path = get_path_in_derive_attr(sema, attr, cursor)?; - let scope = sema.scope(attr.syntax()); - // FIXME: This double resolve shouldn't be necessary - // It's only here so we prefer macros over other namespaces - match scope.speculative_resolve_as_mac(&path) { - Some(mac) if mac.kind() == hir::MacroKind::Derive => Some(PathResolution::Macro(mac)), - Some(_) => return None, - None => scope - .speculative_resolve(&path) - .filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))), - } -} - /// Picks the token with the highest rank returned by the passed in function. pub fn pick_best_token( tokens: TokenAtOffset, diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index d4bc7c7cf87c..2356750bceb8 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs @@ -146,6 +146,7 @@ impl ImportAssets { if let Some(_) = path.qualifier() { return None; } + let name = NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()); let candidate_node = attr.syntax().clone(); Some(Self { From aeb5d6491246fcf53d6cb11bb3035d30580570e7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 2 Jan 2022 16:58:21 +0100 Subject: [PATCH 2/4] Implement ToDef for ast::Attr --- crates/hir/src/semantics.rs | 22 +++++++--------------- crates/hir/src/semantics/source_to_def.rs | 15 ++++++++++++++- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 077ab40d0815..9be1619228cd 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -476,12 +476,12 @@ impl<'db> SemanticsImpl<'db> { } fn derive_macro_calls(&self, attr: &ast::Attr) -> Option>> { - 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 adt = attr.syntax().parent().and_then(ast::Adt::cast)?; + let file_id = self.find_file(adt.syntax()).file_id; + let adt = InFile::new(file_id, &adt); let src = InFile::new(file_id, attr.clone()); self.with_ctx(|ctx| { - let res = ctx.attr_to_derive_macro_call(item, src)?; + let res = ctx.attr_to_derive_macro_call(adt, src)?; Some(res.to_vec()) }) } @@ -909,17 +909,8 @@ impl<'db> SemanticsImpl<'db> { return None; } - // Fetch hir::Attr definition - // FIXME: Move this to ToDef impl? - let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; - let attr_pos = adt.attrs().position(|it| it == attr)?; - let attrs = { - let file_id = self.find_file(adt.syntax()).file_id; - let adt = InFile::new(file_id, adt); - let def = self.with_ctx(|ctx| ctx.adt_to_def(adt))?; - self.db.attrs(def.into()) - }; - let attr_def = attrs.get(attr_pos)?; + let attr_def = + ast::Attr::to_def(self, self.find_file(attr.syntax()).with_value(attr.clone()))?; let mut derive_paths = attr_def.parse_path_comma_token_tree()?; let derives = self.resolve_derive_macro(&attr)?; @@ -1214,6 +1205,7 @@ to_def_impls![ (crate::Local, ast::SelfParam, self_param_to_def), (crate::Label, ast::Label, label_to_def), (crate::Adt, ast::Adt, adt_to_def), + (crate::Attr, ast::Attr, attr_to_def), ]; fn find_root(node: &SyntaxNode) -> SyntaxNode { diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 495c84e65f43..fbce53eb166c 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -210,6 +210,19 @@ impl SourceToDefCtx<'_, '_> { ast::Adt::Union(it) => self.union_to_def(InFile::new(file_id, it)).map(AdtId::UnionId), } } + pub(super) fn attr_to_def( + &mut self, + InFile { file_id, value }: InFile, + ) -> Option { + // FIXME: Use dynmap? + let adt = value.syntax().parent().and_then(ast::Adt::cast)?; + let attr_pos = ast::HasAttrs::attrs(&adt).position(|it| it == value)?; + let attrs = { + let def = self.adt_to_def(InFile::new(file_id, adt))?; + self.db.attrs(def.into()) + }; + attrs.get(attr_pos).cloned() + } pub(super) fn bind_pat_to_def( &mut self, src: InFile, @@ -246,7 +259,7 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn attr_to_derive_macro_call( &mut self, - item: InFile<&ast::Item>, + item: InFile<&ast::Adt>, src: InFile, ) -> Option<&[Option]> { let map = self.dyn_map(item)?; From 44b0fe8ec7360fcf78bf0a0b77d3394d48cc83f1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 2 Jan 2022 17:34:28 +0100 Subject: [PATCH 3/4] cleanup --- crates/hir/src/semantics.rs | 69 ++++++++++++++++++++++--------------- crates/hir_def/src/attr.rs | 6 +++- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 9be1619228cd..33cbf74f5e46 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -5,6 +5,7 @@ mod source_to_def; use std::{cell::RefCell, fmt}; use base_db::{FileId, FileRange}; +use either::Either; use hir_def::{ body, resolver::{self, HasResolver, Resolver, TypeNs}, @@ -909,42 +910,54 @@ impl<'db> SemanticsImpl<'db> { return None; } - let attr_def = - ast::Attr::to_def(self, self.find_file(attr.syntax()).with_value(attr.clone()))?; + let file = self.find_file(attr.syntax()); + let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; - let mut derive_paths = attr_def.parse_path_comma_token_tree()?; - let derives = self.resolve_derive_macro(&attr)?; + let res = self.with_ctx(|ctx| { + let attr_def = ctx.attr_to_def(file.with_value(attr.clone()))?; + let derives = ctx + .attr_to_derive_macro_call(file.with_value(&adt), file.with_value(attr.clone()))?; - let derive_idx = tt - .syntax() - .children_with_tokens() - .filter_map(SyntaxElement::into_token) - .take_while(|tok| tok != syntax) - .filter(|t| t.kind() == T![,]) - .count(); - let path_segment_idx = syntax - .siblings_with_tokens(Direction::Prev) - .filter_map(SyntaxElement::into_token) - .take_while(|tok| matches!(tok.kind(), T![:] | T![ident])) - .filter(|tok| tok.kind() == T![ident]) - .count(); + let mut derive_paths = attr_def.parse_path_comma_token_tree()?; - let mut mod_path = derive_paths.nth(derive_idx)?; + let derive_idx = tt + .syntax() + .children_with_tokens() + .filter_map(SyntaxElement::into_token) + .take_while(|tok| tok != syntax) + .filter(|t| t.kind() == T![,]) + .count(); + let path_segment_idx = syntax + .siblings_with_tokens(Direction::Prev) + .filter_map(SyntaxElement::into_token) + .take_while(|tok| matches!(tok.kind(), T![:] | T![ident])) + .filter(|tok| tok.kind() == T![ident]) + .count(); - if path_segment_idx < mod_path.len() { - // the path for the given ident is a qualifier, resolve to module if possible - while path_segment_idx < mod_path.len() { - mod_path.pop_segment(); + let mut mod_path = derive_paths.nth(derive_idx)?; + + if path_segment_idx < mod_path.len() { + // the path for the given ident is a qualifier, resolve to module if possible + while path_segment_idx < mod_path.len() { + mod_path.pop_segment(); + } + Some(Either::Left(mod_path)) + } else { + // otherwise fetch the derive + Some(Either::Right(derives[derive_idx])) } - resolve_hir_path( + })?; + + match res { + Either::Left(path) => resolve_hir_path( self.db, &self.scope(attr.syntax()).resolver, - &Path::from_known_path(mod_path, []), + &Path::from_known_path(path, []), ) - .filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))) - } else { - // otherwise fetch the derive - derives.get(derive_idx)?.map(PathResolution::Macro) + .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/attr.rs b/crates/hir_def/src/attr.rs index c63cd4c195bf..ca87b501aa8f 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs @@ -746,7 +746,11 @@ impl Attr { }) .collect::>(); - return Some(paths.into_iter()); + Some(paths.into_iter()) + } + + pub fn path(&self) -> &ModPath { + &self.path } pub fn string_value(&self) -> Option<&SmolStr> { From 19f1ff5c7063dac772c1d324d11661393a3b5f7e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 3 Jan 2022 16:00:45 +0100 Subject: [PATCH 4/4] give `resolve_derive_ident` a more robust api --- crates/hir/src/semantics.rs | 37 +++++++++++-------- .../ide/src/syntax_highlighting/highlight.rs | 16 +++++--- crates/ide_db/src/defs.rs | 8 +++- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 33cbf74f5e46..b685d260815d 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -356,8 +356,12 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_bind_pat_to_const(pat) } - pub fn resolve_derive_ident(&self, ident: &ast::Ident) -> Option { - self.imp.resolve_derive_ident(ident) + pub fn resolve_derive_ident( + &self, + derive: &ast::Attr, + ident: &ast::Ident, + ) -> Option { + self.imp.resolve_derive_ident(derive, ident) } // FIXME: use this instead? @@ -900,23 +904,26 @@ impl<'db> SemanticsImpl<'db> { self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat) } - fn resolve_derive_ident(&self, ident: &ast::Ident) -> Option { + fn resolve_derive_ident( + &self, + derive: &ast::Attr, + ident: &ast::Ident, + ) -> Option { + debug_assert!(ident.syntax().parent().and_then(ast::TokenTree::cast).is_some()); + debug_assert!(ident.syntax().ancestors().any(|anc| anc == *derive.syntax())); // derive macros are always at depth 2, tokentree -> meta -> attribute let syntax = ident.syntax(); - let attr = syntax.ancestors().nth(2).and_then(ast::Attr::cast)?; - let tt = attr.token_tree()?; - if !tt.syntax().text_range().contains_range(ident.syntax().text_range()) { - return None; - } - - let file = self.find_file(attr.syntax()); - let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; + let tt = derive.token_tree()?; + let file = self.find_file(derive.syntax()); + let adt = derive.syntax().parent().and_then(ast::Adt::cast)?; let res = self.with_ctx(|ctx| { - let attr_def = ctx.attr_to_def(file.with_value(attr.clone()))?; - let derives = ctx - .attr_to_derive_macro_call(file.with_value(&adt), file.with_value(attr.clone()))?; + let attr_def = ctx.attr_to_def(file.with_value(derive.clone()))?; + let derives = ctx.attr_to_derive_macro_call( + file.with_value(&adt), + file.with_value(derive.clone()), + )?; let mut derive_paths = attr_def.parse_path_comma_token_tree()?; @@ -951,7 +958,7 @@ impl<'db> SemanticsImpl<'db> { match res { Either::Left(path) => resolve_hir_path( self.db, - &self.scope(attr.syntax()).resolver, + &self.scope(derive.syntax()).resolver, &Path::from_known_path(path, []), ) .filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))), diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index f82ccaa1c764..688f181d9160 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -39,11 +39,17 @@ pub(super) fn token( INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), BYTE => HlTag::ByteLiteral.into(), CHAR => HlTag::CharLiteral.into(), - IDENT if parent_matches::(&token) => { - match sema.resolve_derive_ident(&ast::Ident::cast(token).unwrap()) { - Some(res) => highlight_def(sema, krate, Definition::from(res)), - None => HlTag::None.into(), - } + IDENT => { + let tt = ast::TokenTree::cast(token.parent()?)?; + let ident = ast::Ident::cast(token)?; + // from this point on we are inside a token tree, this only happens for identifiers + // that were not mapped down into macro invocations + (|| { + let attr = tt.parent_meta()?.parent_attr()?; + let res = sema.resolve_derive_ident(&attr, &ident)?; + Some(highlight_def(sema, krate, Definition::from(res))) + })() + .unwrap_or_else(|| HlTag::None.into()) } p if p.is_punct() => punctuation(sema, token, p), k if k.is_keyword() => keyword(sema, token, k)?, diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index d69fd9bd2170..1501c4eda53a 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs @@ -55,8 +55,12 @@ impl Definition { let attr = ast::TokenTree::cast(parent.clone()) .and_then(|tt| tt.parent_meta()) .and_then(|meta| meta.parent_attr()); - if let Some(_) = attr { - return sema.resolve_derive_ident(&ident).map(Into::into).into_iter().collect(); + if let Some(attr) = attr { + return sema + .resolve_derive_ident(&attr, &ident) + .map(Into::into) + .into_iter() + .collect(); } } Self::from_node(sema, &parent)