Unnamed repository; edit this file 'description' to name the repository.
Merge #11157
11157: internal: Remove `SemanticScope::speculative_resolve_as_mac` r=Veykril a=Veykril Fixes https://github.com/rust-analyzer/rust-analyzer/issues/11132 Co-authored-by: Lukas Wirth <[email protected]>
bors[bot] 2022-01-04
parent 98df25c · parent 19f1ff5 · commit 68bc12c
-rw-r--r--crates/hir/src/semantics.rs97
-rw-r--r--crates/hir/src/semantics/source_to_def.rs15
-rw-r--r--crates/hir_def/src/attr.rs13
-rw-r--r--crates/hir_def/src/nameres/collector.rs2
-rw-r--r--crates/ide/src/goto_definition.rs12
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs23
-rw-r--r--crates/ide_db/src/defs.rs5
-rw-r--r--crates/ide_db/src/helpers.rs20
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs1
9 files changed, 131 insertions, 57 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 645b10f853..b685d26081 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},
@@ -18,13 +19,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 +356,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.resolve_bind_pat_to_const(pat)
}
+ pub fn resolve_derive_ident(
+ &self,
+ derive: &ast::Attr,
+ ident: &ast::Ident,
+ ) -> Option<PathResolution> {
+ self.imp.resolve_derive_ident(derive, ident)
+ }
+
// FIXME: use this instead?
// pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
@@ -471,12 +481,12 @@ impl<'db> SemanticsImpl<'db> {
}
fn derive_macro_calls(&self, attr: &ast::Attr) -> Option<Vec<Option<MacroCallId>>> {
- 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())
})
}
@@ -894,6 +904,70 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
}
+ fn resolve_derive_ident(
+ &self,
+ derive: &ast::Attr,
+ ident: &ast::Ident,
+ ) -> Option<PathResolution> {
+ 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 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(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()?;
+
+ 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();
+ }
+ Some(Either::Left(mod_path))
+ } else {
+ // otherwise fetch the derive
+ Some(Either::Right(derives[derive_idx]))
+ }
+ })?;
+
+ 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::Right(derive) => derive
+ .map(|call| MacroDef { id: self.db.lookup_intern_macro_call(call).def })
+ .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)
@@ -1151,6 +1225,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 {
@@ -1230,14 +1305,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<MacroDef> {
- 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/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 495c84e65f..fbce53eb16 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<ast::Attr>,
+ ) -> Option<crate::Attr> {
+ // 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<ast::IdentPat>,
@@ -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<ast::Attr>,
) -> Option<&[Option<MacroCallId>]> {
let map = self.dyn_map(item)?;
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 383ad7f0c8..ca87b501aa 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<impl Iterator<Item = ModPath> + '_> {
+ /// Parses this attribute as a token tree consisting of comma separated paths.
+ pub fn parse_path_comma_token_tree(&self) -> Option<impl Iterator<Item = ModPath> + '_> {
let args = match self.input.as_deref() {
Some(AttrInput::TokenTree(args, _)) => args,
_ => return None,
@@ -749,7 +746,11 @@ impl Attr {
})
.collect::<Vec<_>>();
- return Some(paths.into_iter());
+ Some(paths.into_iter())
+ }
+
+ pub fn path(&self) -> &ModPath {
+ &self.path
}
pub fn string_value(&self) -> Option<&SmolStr> {
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index ca8afe8cbf..be749c3b46 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 0e71d5ff6a..4d638e687a 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1384,6 +1384,18 @@ mod foo {
struct Foo;
"#,
);
+ check(
+ r#"
+//- minicore:derive
+mod foo {
+ // ^^^
+ #[rustc_builtin_macro]
+ pub macro Copy {}
+}
+#[derive(foo$0::Copy)]
+struct Foo;
+ "#,
+ );
}
#[test]
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 41b13170bb..84732aef54 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;
@@ -39,16 +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::<ast::TokenTree>(&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(),
- }
- }
- 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 c9b8c94205..1501c4eda5 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)]
@@ -56,7 +56,8 @@ impl Definition {
.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)
+ return sema
+ .resolve_derive_ident(&attr, &ident)
.map(Into::into)
.into_iter()
.collect();
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index e589940dae..344f8db8d0 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<ast::Path> {
})
}
-/// 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<RootDatabase>,
- attr: &ast::Attr,
- cursor: &ast::Ident,
-) -> Option<PathResolution> {
- 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<SyntaxToken>,
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index d4bc7c7cf8..2356750bce 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 {