Check for derive attributes by item path, not derive identifier

This commit is contained in:
Lukas Wirth 2021-11-17 19:46:32 +01:00
parent 32f425d801
commit 91bbc55eed
9 changed files with 174 additions and 207 deletions

View File

@ -8,7 +8,7 @@ use either::Either;
use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile}; use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
use itertools::Itertools; use itertools::Itertools;
use la_arena::ArenaMap; use la_arena::ArenaMap;
use mbe::{syntax_node_to_token_tree, DelimiterKind}; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use syntax::{ use syntax::{
ast::{self, AstNode, HasAttrs, IsString}, ast::{self, AstNode, HasAttrs, IsString},
@ -722,41 +722,35 @@ impl Attr {
/// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths
/// to derive macros. /// to derive macros.
/// ///
/// Returns `None` when the attribute is not a well-formed `#[derive]` attribute. /// 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>> { pub(crate) fn parse_derive(&self) -> Option<impl Iterator<Item = ModPath>> {
if self.path.as_ident() != Some(&hir_expand::name![derive]) { if let Some(AttrInput::TokenTree(args, _)) = self.input.as_deref() {
return None; if args.delimiter_kind() != Some(DelimiterKind::Parenthesis) {
} return None;
match self.input.as_deref() {
Some(AttrInput::TokenTree(args, _)) => {
let mut counter = 0;
let paths = args
.token_trees
.iter()
.group_by(move |tt| {
match tt {
tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
counter += 1;
}
_ => {}
}
counter
})
.into_iter()
.map(|(_, tts)| {
let segments = tts.filter_map(|tt| match tt {
tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
_ => None,
});
ModPath::from_segments(PathKind::Plain, segments)
})
.collect::<Vec<_>>();
Some(paths.into_iter())
} }
_ => None, let mut counter = 0;
let paths = args
.token_trees
.iter()
.group_by(move |tt| {
if let tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })) = tt {
counter += 1;
}
counter
})
.into_iter()
.map(|(_, tts)| {
let segments = tts.filter_map(|tt| match tt {
tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
_ => None,
});
ModPath::from_segments(PathKind::Plain, segments)
})
.collect::<Vec<_>>();
return Some(paths.into_iter());
} }
None
} }
pub fn string_value(&self) -> Option<&SmolStr> { pub fn string_value(&self) -> Option<&SmolStr> {

View File

@ -26,12 +26,16 @@ fn test_copy_expand_in_core() {
check( check(
r#" r#"
#[rustc_builtin_macro] #[rustc_builtin_macro]
macro derive {}
#[rustc_builtin_macro]
macro Copy {} macro Copy {}
#[derive(Copy)] #[derive(Copy)]
struct Foo; struct Foo;
"#, "#,
expect![[r##" expect![[r##"
#[rustc_builtin_macro] #[rustc_builtin_macro]
macro derive {}
#[rustc_builtin_macro]
macro Copy {} macro Copy {}
#[derive(Copy)] #[derive(Copy)]
struct Foo; struct Foo;

View File

@ -31,6 +31,7 @@ fn derive_censoring() {
check( check(
r#" r#"
//- proc_macros: derive_identity //- proc_macros: derive_identity
//- minicore:derive
#[attr1] #[attr1]
#[derive(Foo)] #[derive(Foo)]
#[derive(proc_macros::DeriveIdentity)] #[derive(proc_macros::DeriveIdentity)]

View File

@ -9,7 +9,7 @@ use base_db::{CrateId, Edition, FileId, ProcMacroId};
use cfg::{CfgExpr, CfgOptions}; use cfg::{CfgExpr, CfgOptions};
use hir_expand::{ use hir_expand::{
ast_id_map::FileAstId, ast_id_map::FileAstId,
builtin_attr_macro::{find_builtin_attr, is_builtin_test_or_bench_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,
name::{name, AsName, Name}, name::{name, AsName, Name},
@ -781,7 +781,7 @@ impl DefCollector<'_> {
} }
fn resolve_extern_crate(&self, name: &Name) -> PerNs { fn resolve_extern_crate(&self, name: &Name) -> PerNs {
if name == &name!(self) { if *name == name!(self) {
cov_mark::hit!(extern_crate_self_as); cov_mark::hit!(extern_crate_self_as);
let root = match self.def_map.block { let root = match self.def_map.block {
Some(_) => { Some(_) => {
@ -1105,7 +1105,7 @@ impl DefCollector<'_> {
let mod_dir = self.mod_dirs[&directive.module_id].clone(); let mod_dir = self.mod_dirs[&directive.module_id].clone();
self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id); self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
ModCollector { ModCollector {
def_collector: &mut *self, def_collector: self,
macro_depth: directive.depth, macro_depth: directive.depth,
module_id: directive.module_id, module_id: directive.module_id,
tree_id: TreeId::new(file_id, None), tree_id: TreeId::new(file_id, None),
@ -1121,6 +1121,65 @@ impl DefCollector<'_> {
} }
} }
let def = resolver(ast_id.path.clone()).filter(MacroDefId::is_attribute);
if matches!(
def,
Some(MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. })
if expander.is_derive()
) {
// Resolved to derive
let file_id = ast_id.ast_id.file_id;
let item_tree = self.db.file_item_tree(file_id);
let ast_id: FileAstId<ast::Item> = match *mod_item {
ModItem::Struct(it) => item_tree[it].ast_id.upcast(),
ModItem::Union(it) => item_tree[it].ast_id.upcast(),
ModItem::Enum(it) => item_tree[it].ast_id.upcast(),
_ => {
// Cannot use derive on this item.
// FIXME: diagnose
res = ReachedFixedPoint::No;
return false;
}
};
match attr.parse_derive() {
Some(derive_macros) => {
for path in derive_macros {
let ast_id = AstIdWithPath::new(file_id, ast_id, path);
self.unresolved_macros.push(MacroDirective {
module_id: directive.module_id,
depth: directive.depth + 1,
kind: MacroDirectiveKind::Derive {
ast_id,
derive_attr: attr.id,
},
});
}
}
None => {
// FIXME: diagnose
tracing::debug!("malformed derive: {:?}", attr);
}
}
let mod_dir = self.mod_dirs[&directive.module_id].clone();
self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
ModCollector {
def_collector: &mut *self,
macro_depth: directive.depth,
module_id: directive.module_id,
tree_id: TreeId::new(file_id, None),
item_tree: &item_tree,
mod_dir,
}
.collect(&[*mod_item]);
// Remove the original directive since we resolved it.
res = ReachedFixedPoint::No;
return false;
}
if !self.db.enable_proc_attr_macros() { if !self.db.enable_proc_attr_macros() {
return true; return true;
} }
@ -1138,7 +1197,11 @@ impl DefCollector<'_> {
// Skip #[test]/#[bench] expansion, which would merely result in more memory usage // Skip #[test]/#[bench] expansion, which would merely result in more memory usage
// due to duplicating functions into macro expansions // due to duplicating functions into macro expansions
if is_builtin_test_or_bench_attr(loc.def) { if matches!(
loc.def.kind,
MacroDefKind::BuiltInAttr(expander, _)
if expander.is_test() || expander.is_bench()
) {
let file_id = ast_id.ast_id.file_id; let file_id = ast_id.ast_id.file_id;
let item_tree = self.db.file_item_tree(file_id); let item_tree = self.db.file_item_tree(file_id);
let mod_dir = self.mod_dirs[&directive.module_id].clone(); let mod_dir = self.mod_dirs[&directive.module_id].clone();
@ -1281,7 +1344,7 @@ impl DefCollector<'_> {
for directive in &self.unresolved_macros { for directive in &self.unresolved_macros {
match &directive.kind { match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to } => { MacroDirectiveKind::FnLike { ast_id, expand_to } => {
match macro_call_as_call_id( let macro_call_as_call_id = macro_call_as_call_id(
ast_id, ast_id,
*expand_to, *expand_to,
self.db, self.db,
@ -1297,15 +1360,13 @@ impl DefCollector<'_> {
resolved_res.resolved_def.take_macros() resolved_res.resolved_def.take_macros()
}, },
&mut |_| (), &mut |_| (),
) { );
Ok(_) => (), if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
Err(UnresolvedMacro { path }) => { self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( directive.module_id,
directive.module_id, ast_id.ast_id,
ast_id.ast_id, path,
path, ));
));
}
} }
} }
MacroDirectiveKind::Derive { .. } | MacroDirectiveKind::Attr { .. } => { MacroDirectiveKind::Derive { .. } | MacroDirectiveKind::Attr { .. } => {
@ -1747,26 +1808,23 @@ impl ModCollector<'_, '_> {
}); });
for attr in iter { for attr in iter {
if attr.path.as_ident() == Some(&hir_expand::name![derive]) { if self.is_builtin_or_registered_attr(&attr.path) {
self.collect_derive(attr, mod_item);
} else if self.is_builtin_or_registered_attr(&attr.path) {
continue; continue;
} else {
tracing::debug!("non-builtin attribute {}", attr.path);
let ast_id = AstIdWithPath::new(
self.file_id(),
mod_item.ast_id(self.item_tree),
attr.path.as_ref().clone(),
);
self.def_collector.unresolved_macros.push(MacroDirective {
module_id: self.module_id,
depth: self.macro_depth + 1,
kind: MacroDirectiveKind::Attr { ast_id, attr: attr.clone(), mod_item },
});
return Err(());
} }
tracing::debug!("non-builtin attribute {}", attr.path);
let ast_id = AstIdWithPath::new(
self.file_id(),
mod_item.ast_id(self.item_tree),
attr.path.as_ref().clone(),
);
self.def_collector.unresolved_macros.push(MacroDirective {
module_id: self.module_id,
depth: self.macro_depth + 1,
kind: MacroDirectiveKind::Attr { ast_id, attr: attr.clone(), mod_item },
});
return Err(());
} }
Ok(()) Ok(())
@ -1800,36 +1858,6 @@ impl ModCollector<'_, '_> {
false false
} }
fn collect_derive(&mut self, attr: &Attr, mod_item: ModItem) {
let ast_id: FileAstId<ast::Item> = match mod_item {
ModItem::Struct(it) => self.item_tree[it].ast_id.upcast(),
ModItem::Union(it) => self.item_tree[it].ast_id.upcast(),
ModItem::Enum(it) => self.item_tree[it].ast_id.upcast(),
_ => {
// Cannot use derive on this item.
// FIXME: diagnose
return;
}
};
match attr.parse_derive() {
Some(derive_macros) => {
for path in derive_macros {
let ast_id = AstIdWithPath::new(self.file_id(), ast_id, path);
self.def_collector.unresolved_macros.push(MacroDirective {
module_id: self.module_id,
depth: self.macro_depth + 1,
kind: MacroDirectiveKind::Derive { ast_id, derive_attr: attr.id },
});
}
}
None => {
// FIXME: diagnose
tracing::debug!("malformed derive: {:?}", attr);
}
}
}
/// If `attrs` registers a procedural macro, collects its definition. /// If `attrs` registers a procedural macro, collects its definition.
fn collect_proc_macro_def(&mut self, func_name: &Name, ast_id: AstId<ast::Fn>, attrs: &Attrs) { fn collect_proc_macro_def(&mut self, func_name: &Name, ast_id: AstId<ast::Fn>, attrs: &Attrs) {
// FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere // FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere

View File

@ -669,19 +669,20 @@ pub struct bar;
fn expand_derive() { fn expand_derive() {
let map = compute_crate_def_map( let map = compute_crate_def_map(
r#" r#"
//- /main.rs crate:main deps:core //- /main.rs crate:main deps:core
use core::Copy; use core::Copy;
#[derive(Copy, core::Clone)] #[core::derive(Copy, core::Clone)]
struct Foo; struct Foo;
//- /core.rs crate:core //- /core.rs crate:core
#[rustc_builtin_macro] #[rustc_builtin_macro]
pub macro Copy {} pub macro derive($item:item) {}
#[rustc_builtin_macro]
#[rustc_builtin_macro] pub macro Copy {}
pub macro Clone {} #[rustc_builtin_macro]
"#, pub macro Clone {}
"#,
); );
assert_eq!(map.modules[map.root].scope.impls().len(), 2); assert_eq!(map.modules[map.root].scope.impls().len(), 2);
} }
@ -712,17 +713,19 @@ fn builtin_derive_with_unresolved_attributes_fall_back() {
cov_mark::check!(unresolved_attribute_fallback); cov_mark::check!(unresolved_attribute_fallback);
let map = compute_crate_def_map( let map = compute_crate_def_map(
r#" r#"
//- /main.rs crate:main deps:core //- /main.rs crate:main deps:core
use core::Clone; use core::{Clone, derive};
#[derive(Clone)] #[derive(Clone)]
#[unresolved] #[unresolved]
struct Foo; struct Foo;
//- /core.rs crate:core //- /core.rs crate:core
#[rustc_builtin_macro] #[rustc_builtin_macro]
pub macro Clone {} pub macro derive($item:item) {}
"#, #[rustc_builtin_macro]
pub macro Clone {}
"#,
); );
assert_eq!(map.modules[map.root].scope.impls().len(), 1); assert_eq!(map.modules[map.root].scope.impls().len(), 1);
} }
@ -799,6 +802,9 @@ fn resolves_derive_helper() {
check( check(
r#" r#"
//- /main.rs crate:main deps:proc //- /main.rs crate:main deps:proc
#[rustc_builtin_macro]
pub macro derive($item:item) {}
#[derive(proc::Derive)] #[derive(proc::Derive)]
#[helper] #[helper]
#[unresolved] #[unresolved]
@ -811,6 +817,7 @@ fn derive() {}
expect![[r#" expect![[r#"
crate crate
S: t v S: t v
derive: m
"#]], "#]],
); );
} }

View File

@ -36,6 +36,18 @@ macro_rules! register_builtin {
}; };
} }
impl BuiltinAttrExpander {
pub fn is_derive(self) -> bool {
matches!(self, BuiltinAttrExpander::Derive)
}
pub fn is_test(self) -> bool {
matches!(self, BuiltinAttrExpander::Test)
}
pub fn is_bench(self) -> bool {
matches!(self, BuiltinAttrExpander::Bench)
}
}
register_builtin! { register_builtin! {
(bench, Bench) => dummy_attr_expand, (bench, Bench) => dummy_attr_expand,
(cfg_accessible, CfgAccessible) => dummy_attr_expand, (cfg_accessible, CfgAccessible) => dummy_attr_expand,
@ -46,16 +58,6 @@ register_builtin! {
(test_case, TestCase) => dummy_attr_expand (test_case, TestCase) => dummy_attr_expand
} }
pub fn is_builtin_test_or_bench_attr(makro: MacroDefId) -> bool {
match makro.kind {
MacroDefKind::BuiltInAttr(expander, ..) => {
BuiltinAttrExpander::find_by_name(&name!(test)) == Some(expander)
|| BuiltinAttrExpander::find_by_name(&name!(bench)) == Some(expander)
}
_ => false,
}
}
pub fn find_builtin_attr( pub fn find_builtin_attr(
ident: &name::Name, ident: &name::Name,
krate: CrateId, krate: CrateId,

View File

@ -974,61 +974,12 @@ fn infer_builtin_macros_env() {
fn infer_derive_clone_simple() { fn infer_derive_clone_simple() {
check_types( check_types(
r#" r#"
//- /main.rs crate:main deps:core //- minicore: derive, clone
#[derive(Clone)] #[derive(Clone)]
struct S; struct S;
fn test() { fn test() {
S.clone(); S.clone();
} //^^^^^^^^^ S } //^^^^^^^^^ S
//- /lib.rs crate:core
pub mod prelude {
pub mod rust_2018 {
#[rustc_builtin_macro]
pub macro Clone {}
pub use crate::clone::Clone;
}
}
pub mod clone {
pub trait Clone {
fn clone(&self) -> Self;
}
}
"#,
);
}
#[test]
fn infer_derive_clone_in_core() {
check_types(
r#"
//- /lib.rs crate:core
#[prelude_import]
use prelude::rust_2018::*;
pub mod prelude {
pub mod rust_2018 {
#[rustc_builtin_macro]
pub macro Clone {}
pub use crate::clone::Clone;
}
}
pub mod clone {
pub trait Clone {
fn clone(&self) -> Self;
}
}
#[derive(Clone)]
pub struct S;
//- /main.rs crate:main deps:core
use core::S;
fn test() {
S.clone();
} //^^^^^^^^^ S
"#, "#,
); );
} }
@ -1037,7 +988,7 @@ fn test() {
fn infer_derive_clone_with_params() { fn infer_derive_clone_with_params() {
check_types( check_types(
r#" r#"
//- /main.rs crate:main deps:core //- minicore: clone, derive
#[derive(Clone)] #[derive(Clone)]
struct S; struct S;
#[derive(Clone)] #[derive(Clone)]
@ -1048,21 +999,6 @@ fn test() {
x; x;
//^ (Wrapper<S>, {unknown}) //^ (Wrapper<S>, {unknown})
} }
//- /lib.rs crate:core
pub mod prelude {
pub mod rust_2018 {
#[rustc_builtin_macro]
pub macro Clone {}
pub use crate::clone::Clone;
}
}
pub mod clone {
pub trait Clone {
fn clone(&self) -> Self;
}
}
"#, "#,
); );
} }
@ -1072,7 +1008,7 @@ fn infer_custom_derive_simple() {
// FIXME: this test current now do nothing // FIXME: this test current now do nothing
check_types( check_types(
r#" r#"
//- /main.rs crate:main //- minicore: derive
use foo::Foo; use foo::Foo;
#[derive(Foo)] #[derive(Foo)]

View File

@ -367,9 +367,7 @@ fn main() {
check( check(
r#" r#"
//- proc_macros: identity //- proc_macros: identity
//- minicore: clone, derive
#[rustc_builtin_macro]
pub macro Clone {}
#[proc_macros::identity] #[proc_macros::identity]
#[derive(C$0lone)] #[derive(C$0lone)]
@ -377,7 +375,7 @@ struct Foo {}
"#, "#,
expect![[r#" expect![[r#"
Clone Clone
impl< >crate::clone::Clone for Foo< >{} impl< >core::clone::Clone for Foo< >{}
"#]], "#]],
); );
@ -387,10 +385,7 @@ struct Foo {}
fn macro_expand_derive2() { fn macro_expand_derive2() {
check( check(
r#" r#"
#[rustc_builtin_macro] //- minicore: copy, clone, derive
pub macro Clone {}
#[rustc_builtin_macro]
pub macro Copy {}
#[derive(Cop$0y)] #[derive(Cop$0y)]
#[derive(Clone)] #[derive(Clone)]
@ -398,7 +393,7 @@ struct Foo {}
"#, "#,
expect![[r#" expect![[r#"
Copy Copy
impl< >crate::marker::Copy for Foo< >{} impl< >core::marker::Copy for Foo< >{}
"#]], "#]],
); );
@ -408,19 +403,16 @@ struct Foo {}
fn macro_expand_derive_multi() { fn macro_expand_derive_multi() {
check( check(
r#" r#"
#[rustc_builtin_macro] //- minicore: copy, clone, derive
pub macro Clone {}
#[rustc_builtin_macro]
pub macro Copy {}
#[derive(Cop$0y, Clone)] #[derive(Cop$0y, Clone)]
struct Foo {} struct Foo {}
"#, "#,
expect![[r#" expect![[r#"
Copy, Clone Copy, Clone
impl< >crate::marker::Copy for Foo< >{} impl< >core::marker::Copy for Foo< >{}
impl< >crate::clone::Clone for Foo< >{} impl< >core::clone::Clone for Foo< >{}
"#]], "#]],
); );

View File

@ -805,6 +805,9 @@ bar = {path = "../bar"}
//- /foo/src/main.rs //- /foo/src/main.rs
use bar::Bar; use bar::Bar;
#[rustc_builtin_macro]
macro derive($item:item) {}
trait Bar { trait Bar {
fn bar(); fn bar();
} }