X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=crates%2Fide_db%2Fsrc%2Fhelpers.rs;h=f6a1a5521836a8a1e41e258a217bae04301be4ed;hb=3018ffd85e6921ba57d4340f666269ec85e58902;hp=a8087d47fe269a5deb274fa696e1ca8469311759;hpb=b6ed91a6de846682d91b7196e4fb0c19106d1654;p=rust.git diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index a8087d47fe2..f6a1a552183 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs @@ -7,14 +7,16 @@ pub mod node_ext; pub mod rust_doc; -use std::collections::VecDeque; +use std::{collections::VecDeque, iter}; use base_db::FileId; use either::Either; -use hir::{ItemInNs, MacroDef, ModuleDef, Name, Semantics}; +use hir::{ItemInNs, MacroDef, ModuleDef, Name, PathResolution, Semantics}; +use itertools::Itertools; use syntax::{ - ast::{self, make, HasLoopBody}, - AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent, T, + ast::{self, make, HasLoopBody, Ident}, + AstNode, AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent, + T, }; use crate::RootDatabase; @@ -29,35 +31,58 @@ pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option { } } -/// 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( +/// Parses and returns the derive 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. +/// +/// The returned path is synthesized from TokenTree tokens and as such cannot be used with the [`Semantics`]. +pub fn get_path_in_derive_attr( sema: &hir::Semantics, - derive_attr: &ast::Attr, - cursor: &SyntaxToken, -) -> Option { - use itertools::Itertools; - if cursor.kind() != T![ident] { + attr: &ast::Attr, + cursor: &Ident, +) -> Option { + let cursor = cursor.syntax(); + let path = attr.path()?; + let tt = attr.token_tree()?; + if !tt.syntax().text_range().contains_range(cursor.text_range()) { 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 + let scope = sema.scope(attr.syntax()); + let resolved_attr = sema.resolve_path(&path)?; + let derive = FamousDefs(sema, scope.krate()).core_macros_builtin_derive()?; + if PathResolution::Macro(derive) != resolved_attr { + return None; + } + + let first = cursor .siblings_with_tokens(Direction::Prev) - .flat_map(SyntaxElement::into_token) + .filter_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, + .last()?; + let path_tokens = first + .siblings_with_tokens(Direction::Next) + .filter_map(SyntaxElement::into_token) + .take_while(|tok| tok != cursor); + + ast::Path::parse(&path_tokens.chain(iter::once(cursor.clone())).join("")).ok() +} + +/// 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: &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(_)))), } }