Resolve $crate in derive paths

This commit is contained in:
Ryo Yoshida 2023-04-20 00:54:01 +09:00
parent 2400b36a2e
commit cf72b6232b
No known key found for this signature in database
GPG key ID: E25698A930586171
3 changed files with 56 additions and 17 deletions

View file

@ -14,6 +14,7 @@ use hir_expand::{
builtin_attr_macro::find_builtin_attr, builtin_attr_macro::find_builtin_attr,
builtin_derive_macro::find_builtin_derive, builtin_derive_macro::find_builtin_derive,
builtin_fn_macro::find_builtin_macro, builtin_fn_macro::find_builtin_macro,
hygiene::Hygiene,
name::{name, AsName, Name}, name::{name, AsName, Name},
proc_macro::ProcMacroExpander, proc_macro::ProcMacroExpander,
ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc,
@ -312,13 +313,14 @@ impl DefCollector<'_> {
} }
if *attr_name == hir_expand::name![feature] { if *attr_name == hir_expand::name![feature] {
let features = let features = attr
attr.parse_path_comma_token_tree().into_iter().flatten().filter_map( .parse_path_comma_token_tree(self.db.upcast(), Hygiene::new_unhygienic())
|feat| match feat.segments() { .into_iter()
[name] => Some(name.to_smol_str()), .flatten()
_ => None, .filter_map(|feat| match feat.segments() {
}, [name] => Some(name.to_smol_str()),
); _ => None,
});
self.def_map.unstable_features.extend(features); self.def_map.unstable_features.extend(features);
} }
@ -1223,8 +1225,9 @@ impl DefCollector<'_> {
} }
}; };
let ast_id = ast_id.with_value(ast_adt_id); 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) => { Some(derive_macros) => {
let mut len = 0; let mut len = 0;
for (idx, path) in derive_macros.enumerate() { for (idx, path) in derive_macros.enumerate() {

View file

@ -664,6 +664,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] #[test]
fn expand_derive() { fn expand_derive() {
let map = compute_crate_def_map( let map = compute_crate_def_map(

View file

@ -12,8 +12,7 @@ use syntax::{ast, match_ast, AstNode, SmolStr, SyntaxNode};
use crate::{ use crate::{
db::ExpandDatabase, db::ExpandDatabase,
hygiene::Hygiene, hygiene::Hygiene,
mod_path::{ModPath, PathKind}, mod_path::ModPath,
name::AsName,
tt::{self, Subtree}, tt::{self, Subtree},
InFile, InFile,
}; };
@ -267,7 +266,11 @@ impl Attr {
} }
/// Parses this attribute as a token tree consisting of comma separated paths. /// 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()?; let args = self.token_tree_value()?;
if args.delimiter.kind != DelimiterKind::Parenthesis { if args.delimiter.kind != DelimiterKind::Parenthesis {
@ -276,15 +279,25 @@ impl Attr {
let paths = args let paths = args
.token_trees .token_trees
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))) .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
.filter_map(|tts| { .filter_map(move |tts| {
if tts.is_empty() { if tts.is_empty() {
return None; return None;
} }
let segments = tts.iter().filter_map(|tt| match tt { // FIXME: This is necessarily a hack. It'd be nice if we could avoid allocation here.
tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()), let subtree = tt::Subtree {
_ => None, delimiter: tt::Delimiter::unspecified(),
}); token_trees: tts.into_iter().cloned().collect(),
Some(ModPath::from_segments(PathKind::Plain, segments)) };
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) Some(paths)