Skip to content

Commit 68bc12c

Browse files
bors[bot]Veykril
andauthored
Merge #11157
11157: internal: Remove `SemanticScope::speculative_resolve_as_mac` r=Veykril a=Veykril Fixes #11132 Co-authored-by: Lukas Wirth <[email protected]>
2 parents 98df25c + 19f1ff5 commit 68bc12c

File tree

9 files changed

+131
-57
lines changed

9 files changed

+131
-57
lines changed

crates/hir/src/semantics.rs

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod source_to_def;
55
use std::{cell::RefCell, fmt};
66

77
use base_db::{FileId, FileRange};
8+
use either::Either;
89
use hir_def::{
910
body,
1011
resolver::{self, HasResolver, Resolver, TypeNs},
@@ -18,13 +19,14 @@ use smallvec::{smallvec, SmallVec};
1819
use syntax::{
1920
algo::skip_trivia_token,
2021
ast::{self, HasAttrs, HasGenericParams, HasLoopBody},
21-
match_ast, AstNode, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize,
22+
match_ast, AstNode, AstToken, Direction, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
23+
TextSize, T,
2224
};
2325

2426
use crate::{
2527
db::HirDatabase,
2628
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
27-
source_analyzer::{resolve_hir_path, resolve_hir_path_as_macro, SourceAnalyzer},
29+
source_analyzer::{resolve_hir_path, SourceAnalyzer},
2830
Access, AssocItem, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource,
2931
HirFileId, Impl, InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path,
3032
ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
@@ -354,6 +356,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
354356
self.imp.resolve_bind_pat_to_const(pat)
355357
}
356358

359+
pub fn resolve_derive_ident(
360+
&self,
361+
derive: &ast::Attr,
362+
ident: &ast::Ident,
363+
) -> Option<PathResolution> {
364+
self.imp.resolve_derive_ident(derive, ident)
365+
}
366+
357367
// FIXME: use this instead?
358368
// pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
359369

@@ -471,12 +481,12 @@ impl<'db> SemanticsImpl<'db> {
471481
}
472482

473483
fn derive_macro_calls(&self, attr: &ast::Attr) -> Option<Vec<Option<MacroCallId>>> {
474-
let item = attr.syntax().parent().and_then(ast::Item::cast)?;
475-
let file_id = self.find_file(item.syntax()).file_id;
476-
let item = InFile::new(file_id, &item);
484+
let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
485+
let file_id = self.find_file(adt.syntax()).file_id;
486+
let adt = InFile::new(file_id, &adt);
477487
let src = InFile::new(file_id, attr.clone());
478488
self.with_ctx(|ctx| {
479-
let res = ctx.attr_to_derive_macro_call(item, src)?;
489+
let res = ctx.attr_to_derive_macro_call(adt, src)?;
480490
Some(res.to_vec())
481491
})
482492
}
@@ -894,6 +904,70 @@ impl<'db> SemanticsImpl<'db> {
894904
self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
895905
}
896906

907+
fn resolve_derive_ident(
908+
&self,
909+
derive: &ast::Attr,
910+
ident: &ast::Ident,
911+
) -> Option<PathResolution> {
912+
debug_assert!(ident.syntax().parent().and_then(ast::TokenTree::cast).is_some());
913+
debug_assert!(ident.syntax().ancestors().any(|anc| anc == *derive.syntax()));
914+
// derive macros are always at depth 2, tokentree -> meta -> attribute
915+
let syntax = ident.syntax();
916+
917+
let tt = derive.token_tree()?;
918+
let file = self.find_file(derive.syntax());
919+
let adt = derive.syntax().parent().and_then(ast::Adt::cast)?;
920+
921+
let res = self.with_ctx(|ctx| {
922+
let attr_def = ctx.attr_to_def(file.with_value(derive.clone()))?;
923+
let derives = ctx.attr_to_derive_macro_call(
924+
file.with_value(&adt),
925+
file.with_value(derive.clone()),
926+
)?;
927+
928+
let mut derive_paths = attr_def.parse_path_comma_token_tree()?;
929+
930+
let derive_idx = tt
931+
.syntax()
932+
.children_with_tokens()
933+
.filter_map(SyntaxElement::into_token)
934+
.take_while(|tok| tok != syntax)
935+
.filter(|t| t.kind() == T![,])
936+
.count();
937+
let path_segment_idx = syntax
938+
.siblings_with_tokens(Direction::Prev)
939+
.filter_map(SyntaxElement::into_token)
940+
.take_while(|tok| matches!(tok.kind(), T![:] | T![ident]))
941+
.filter(|tok| tok.kind() == T![ident])
942+
.count();
943+
944+
let mut mod_path = derive_paths.nth(derive_idx)?;
945+
946+
if path_segment_idx < mod_path.len() {
947+
// the path for the given ident is a qualifier, resolve to module if possible
948+
while path_segment_idx < mod_path.len() {
949+
mod_path.pop_segment();
950+
}
951+
Some(Either::Left(mod_path))
952+
} else {
953+
// otherwise fetch the derive
954+
Some(Either::Right(derives[derive_idx]))
955+
}
956+
})?;
957+
958+
match res {
959+
Either::Left(path) => resolve_hir_path(
960+
self.db,
961+
&self.scope(derive.syntax()).resolver,
962+
&Path::from_known_path(path, []),
963+
)
964+
.filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))),
965+
Either::Right(derive) => derive
966+
.map(|call| MacroDef { id: self.db.lookup_intern_macro_call(call).def })
967+
.map(PathResolution::Macro),
968+
}
969+
}
970+
897971
fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
898972
self.analyze(literal.syntax())
899973
.record_literal_missing_fields(self.db, literal)
@@ -1151,6 +1225,7 @@ to_def_impls![
11511225
(crate::Local, ast::SelfParam, self_param_to_def),
11521226
(crate::Label, ast::Label, label_to_def),
11531227
(crate::Adt, ast::Adt, adt_to_def),
1228+
(crate::Attr, ast::Attr, attr_to_def),
11541229
];
11551230

11561231
fn find_root(node: &SyntaxNode) -> SyntaxNode {
@@ -1230,14 +1305,4 @@ impl<'a> SemanticsScope<'a> {
12301305
let path = Path::from_src(path.clone(), &ctx)?;
12311306
resolve_hir_path(self.db, &self.resolver, &path)
12321307
}
1233-
1234-
/// Resolve a path as-if it was written at the given scope. This is
1235-
/// necessary a heuristic, as it doesn't take hygiene into account.
1236-
// FIXME: This special casing solely exists for attributes for now
1237-
// ideally we should have a path resolution infra that properly knows about overlapping namespaces
1238-
pub fn speculative_resolve_as_mac(&self, path: &ast::Path) -> Option<MacroDef> {
1239-
let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id);
1240-
let path = Path::from_src(path.clone(), &ctx)?;
1241-
resolve_hir_path_as_macro(self.db, &self.resolver, &path)
1242-
}
12431308
}

crates/hir/src/semantics/source_to_def.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,19 @@ impl SourceToDefCtx<'_, '_> {
210210
ast::Adt::Union(it) => self.union_to_def(InFile::new(file_id, it)).map(AdtId::UnionId),
211211
}
212212
}
213+
pub(super) fn attr_to_def(
214+
&mut self,
215+
InFile { file_id, value }: InFile<ast::Attr>,
216+
) -> Option<crate::Attr> {
217+
// FIXME: Use dynmap?
218+
let adt = value.syntax().parent().and_then(ast::Adt::cast)?;
219+
let attr_pos = ast::HasAttrs::attrs(&adt).position(|it| it == value)?;
220+
let attrs = {
221+
let def = self.adt_to_def(InFile::new(file_id, adt))?;
222+
self.db.attrs(def.into())
223+
};
224+
attrs.get(attr_pos).cloned()
225+
}
213226
pub(super) fn bind_pat_to_def(
214227
&mut self,
215228
src: InFile<ast::IdentPat>,
@@ -246,7 +259,7 @@ impl SourceToDefCtx<'_, '_> {
246259

247260
pub(super) fn attr_to_derive_macro_call(
248261
&mut self,
249-
item: InFile<&ast::Item>,
262+
item: InFile<&ast::Adt>,
250263
src: InFile<ast::Attr>,
251264
) -> Option<&[Option<MacroCallId>]> {
252265
let map = self.dyn_map(item)?;

crates/hir_def/src/attr.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -720,11 +720,8 @@ impl Attr {
720720
Self::from_src(db, ast, hygiene, id)
721721
}
722722

723-
/// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths
724-
/// to derive macros.
725-
///
726-
/// Returns `None` when the attribute does not have a well-formed `#[derive]` attribute input.
727-
pub(crate) fn parse_derive(&self) -> Option<impl Iterator<Item = ModPath> + '_> {
723+
/// Parses this attribute as a token tree consisting of comma separated paths.
724+
pub fn parse_path_comma_token_tree(&self) -> Option<impl Iterator<Item = ModPath> + '_> {
728725
let args = match self.input.as_deref() {
729726
Some(AttrInput::TokenTree(args, _)) => args,
730727
_ => return None,
@@ -749,7 +746,11 @@ impl Attr {
749746
})
750747
.collect::<Vec<_>>();
751748

752-
return Some(paths.into_iter());
749+
Some(paths.into_iter())
750+
}
751+
752+
pub fn path(&self) -> &ModPath {
753+
&self.path
753754
}
754755

755756
pub fn string_value(&self) -> Option<&SmolStr> {

crates/hir_def/src/nameres/collector.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,7 @@ impl DefCollector<'_> {
11451145
}
11461146
}
11471147

1148-
match attr.parse_derive() {
1148+
match attr.parse_path_comma_token_tree() {
11491149
Some(derive_macros) => {
11501150
let mut len = 0;
11511151
for (idx, path) in derive_macros.enumerate() {

crates/ide/src/goto_definition.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,6 +1381,18 @@ mod foo {
13811381
// ^^^^
13821382
}
13831383
#[derive(foo::Copy$0)]
1384+
struct Foo;
1385+
"#,
1386+
);
1387+
check(
1388+
r#"
1389+
//- minicore:derive
1390+
mod foo {
1391+
// ^^^
1392+
#[rustc_builtin_macro]
1393+
pub macro Copy {}
1394+
}
1395+
#[derive(foo$0::Copy)]
13841396
struct Foo;
13851397
"#,
13861398
);

crates/ide/src/syntax_highlighting/highlight.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use hir::{AsAssocItem, HasVisibility, Semantics};
44
use ide_db::{
55
defs::{Definition, NameClass, NameRefClass},
6-
helpers::{try_resolve_derive_input, FamousDefs},
6+
helpers::FamousDefs,
77
RootDatabase, SymbolKind,
88
};
99
use rustc_hash::FxHashMap;
@@ -39,16 +39,17 @@ pub(super) fn token(
3939
INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
4040
BYTE => HlTag::ByteLiteral.into(),
4141
CHAR => HlTag::CharLiteral.into(),
42-
IDENT if parent_matches::<ast::TokenTree>(&token) => {
43-
match token.ancestors().nth(2).and_then(ast::Attr::cast) {
44-
Some(attr) => {
45-
match try_resolve_derive_input(sema, &attr, &ast::Ident::cast(token).unwrap()) {
46-
Some(res) => highlight_def(sema, krate, Definition::from(res)),
47-
None => HlTag::None.into(),
48-
}
49-
}
50-
None => HlTag::None.into(),
51-
}
42+
IDENT => {
43+
let tt = ast::TokenTree::cast(token.parent()?)?;
44+
let ident = ast::Ident::cast(token)?;
45+
// from this point on we are inside a token tree, this only happens for identifiers
46+
// that were not mapped down into macro invocations
47+
(|| {
48+
let attr = tt.parent_meta()?.parent_attr()?;
49+
let res = sema.resolve_derive_ident(&attr, &ident)?;
50+
Some(highlight_def(sema, krate, Definition::from(res)))
51+
})()
52+
.unwrap_or_else(|| HlTag::None.into())
5253
}
5354
p if p.is_punct() => punctuation(sema, token, p),
5455
k if k.is_keyword() => keyword(sema, token, k)?,

crates/ide_db/src/defs.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use syntax::{
1717
match_ast, AstToken, SyntaxKind, SyntaxNode, SyntaxToken,
1818
};
1919

20-
use crate::{helpers::try_resolve_derive_input, RootDatabase};
20+
use crate::RootDatabase;
2121

2222
// FIXME: a more precise name would probably be `Symbol`?
2323
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
@@ -56,7 +56,8 @@ impl Definition {
5656
.and_then(|tt| tt.parent_meta())
5757
.and_then(|meta| meta.parent_attr());
5858
if let Some(attr) = attr {
59-
return try_resolve_derive_input(&sema, &attr, &ident)
59+
return sema
60+
.resolve_derive_ident(&attr, &ident)
6061
.map(Into::into)
6162
.into_iter()
6263
.collect();

crates/ide_db/src/helpers.rs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,26 +74,6 @@ pub fn get_path_at_cursor_in_tt(cursor: &ast::Ident) -> Option<ast::Path> {
7474
})
7575
}
7676

77-
/// Parses and resolves the path at the cursor position in the given attribute, if it is a derive.
78-
/// This special case is required because the derive macro is a compiler builtin that discards the input derives.
79-
pub fn try_resolve_derive_input(
80-
sema: &hir::Semantics<RootDatabase>,
81-
attr: &ast::Attr,
82-
cursor: &ast::Ident,
83-
) -> Option<PathResolution> {
84-
let path = get_path_in_derive_attr(sema, attr, cursor)?;
85-
let scope = sema.scope(attr.syntax());
86-
// FIXME: This double resolve shouldn't be necessary
87-
// It's only here so we prefer macros over other namespaces
88-
match scope.speculative_resolve_as_mac(&path) {
89-
Some(mac) if mac.kind() == hir::MacroKind::Derive => Some(PathResolution::Macro(mac)),
90-
Some(_) => return None,
91-
None => scope
92-
.speculative_resolve(&path)
93-
.filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))),
94-
}
95-
}
96-
9777
/// Picks the token with the highest rank returned by the passed in function.
9878
pub fn pick_best_token(
9979
tokens: TokenAtOffset<SyntaxToken>,

crates/ide_db/src/helpers/import_assets.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ impl ImportAssets {
146146
if let Some(_) = path.qualifier() {
147147
return None;
148148
}
149+
149150
let name = NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string());
150151
let candidate_node = attr.syntax().clone();
151152
Some(Self {

0 commit comments

Comments
 (0)