Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-def/src/nameres/collector.rs19
-rw-r--r--crates/hir-def/src/nameres/tests/macros.rs23
-rw-r--r--crates/hir-expand/src/attrs.rs31
3 files changed, 56 insertions, 17 deletions
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 756a8f5049..e845cde73a 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -14,6 +14,7 @@ use hir_expand::{
builtin_attr_macro::find_builtin_attr,
builtin_derive_macro::find_builtin_derive,
builtin_fn_macro::find_builtin_macro,
+ hygiene::Hygiene,
name::{name, AsName, Name},
proc_macro::ProcMacroExpander,
ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc,
@@ -312,13 +313,14 @@ impl DefCollector<'_> {
}
if *attr_name == hir_expand::name![feature] {
- let features =
- attr.parse_path_comma_token_tree().into_iter().flatten().filter_map(
- |feat| match feat.segments() {
- [name] => Some(name.to_smol_str()),
- _ => None,
- },
- );
+ let features = attr
+ .parse_path_comma_token_tree(self.db.upcast(), Hygiene::new_unhygienic())
+ .into_iter()
+ .flatten()
+ .filter_map(|feat| match feat.segments() {
+ [name] => Some(name.to_smol_str()),
+ _ => None,
+ });
self.def_map.unstable_features.extend(features);
}
@@ -1223,8 +1225,9 @@ impl DefCollector<'_> {
}
};
let ast_id = ast_id.with_value(ast_adt_id);
+ let hygiene = Hygiene::new(self.db.upcast(), file_id);
- match attr.parse_path_comma_token_tree() {
+ match attr.parse_path_comma_token_tree(self.db.upcast(), hygiene) {
Some(derive_macros) => {
let mut len = 0;
for (idx, path) in derive_macros.enumerate() {
diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs
index a4ccd14cbb..6ee56c9368 100644
--- a/crates/hir-def/src/nameres/tests/macros.rs
+++ b/crates/hir-def/src/nameres/tests/macros.rs
@@ -665,6 +665,29 @@ pub struct bar;
}
#[test]
+fn macro_dollar_crate_is_correct_in_derive_meta() {
+ let map = compute_crate_def_map(
+ r#"
+//- minicore: derive, clone
+//- /main.rs crate:main deps:lib
+lib::foo!();
+
+//- /lib.rs crate:lib
+#[macro_export]
+macro_rules! foo {
+ () => {
+ #[derive($crate::Clone)]
+ struct S;
+ }
+}
+
+pub use core::clone::Clone;
+"#,
+ );
+ assert_eq!(map.modules[map.root].scope.impls().len(), 1);
+}
+
+#[test]
fn expand_derive() {
let map = compute_crate_def_map(
r#"
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index 7a61ca4f4d..fd0254248b 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -12,8 +12,7 @@ use syntax::{ast, match_ast, AstNode, SmolStr, SyntaxNode};
use crate::{
db::ExpandDatabase,
hygiene::Hygiene,
- mod_path::{ModPath, PathKind},
- name::AsName,
+ mod_path::ModPath,
tt::{self, Subtree},
InFile,
};
@@ -267,7 +266,11 @@ impl Attr {
}
/// 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> + '_> {
+ pub fn parse_path_comma_token_tree<'a>(
+ &'a self,
+ db: &'a dyn ExpandDatabase,
+ hygiene: Hygiene,
+ ) -> Option<impl Iterator<Item = ModPath> + 'a> {
let args = self.token_tree_value()?;
if args.delimiter.kind != DelimiterKind::Parenthesis {
@@ -276,15 +279,25 @@ impl Attr {
let paths = args
.token_trees
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
- .filter_map(|tts| {
+ .filter_map(move |tts| {
if tts.is_empty() {
return None;
}
- let segments = tts.iter().filter_map(|tt| match tt {
- tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
- _ => None,
- });
- Some(ModPath::from_segments(PathKind::Plain, segments))
+ // FIXME: This is necessarily a hack. It'd be nice if we could avoid allocation here.
+ let subtree = tt::Subtree {
+ delimiter: tt::Delimiter::unspecified(),
+ token_trees: tts.into_iter().cloned().collect(),
+ };
+ let (parse, _) =
+ mbe::token_tree_to_syntax_node(&subtree, mbe::TopEntryPoint::MetaItem);
+ let meta = ast::Meta::cast(parse.syntax_node())?;
+ // Only simple paths are allowed.
+ if meta.eq_token().is_some() || meta.expr().is_some() || meta.token_tree().is_some()
+ {
+ return None;
+ }
+ let path = meta.path()?;
+ ModPath::from_src(db, path, &hygiene)
});
Some(paths)