mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 11:20:54 +00:00
Check for derive attributes by item path, not derive
identifier
This commit is contained in:
parent
32f425d801
commit
91bbc55eed
@ -8,7 +8,7 @@ use either::Either;
|
||||
use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
|
||||
use itertools::Itertools;
|
||||
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 syntax::{
|
||||
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
|
||||
/// 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>> {
|
||||
if self.path.as_ident() != Some(&hir_expand::name![derive]) {
|
||||
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())
|
||||
if let Some(AttrInput::TokenTree(args, _)) = self.input.as_deref() {
|
||||
if args.delimiter_kind() != Some(DelimiterKind::Parenthesis) {
|
||||
return None;
|
||||
}
|
||||
_ => 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> {
|
||||
|
@ -26,12 +26,16 @@ fn test_copy_expand_in_core() {
|
||||
check(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro derive {}
|
||||
#[rustc_builtin_macro]
|
||||
macro Copy {}
|
||||
#[derive(Copy)]
|
||||
struct Foo;
|
||||
"#,
|
||||
expect![[r##"
|
||||
#[rustc_builtin_macro]
|
||||
macro derive {}
|
||||
#[rustc_builtin_macro]
|
||||
macro Copy {}
|
||||
#[derive(Copy)]
|
||||
struct Foo;
|
||||
|
@ -31,6 +31,7 @@ fn derive_censoring() {
|
||||
check(
|
||||
r#"
|
||||
//- proc_macros: derive_identity
|
||||
//- minicore:derive
|
||||
#[attr1]
|
||||
#[derive(Foo)]
|
||||
#[derive(proc_macros::DeriveIdentity)]
|
||||
|
@ -9,7 +9,7 @@ use base_db::{CrateId, Edition, FileId, ProcMacroId};
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use hir_expand::{
|
||||
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_fn_macro::find_builtin_macro,
|
||||
name::{name, AsName, Name},
|
||||
@ -781,7 +781,7 @@ impl DefCollector<'_> {
|
||||
}
|
||||
|
||||
fn resolve_extern_crate(&self, name: &Name) -> PerNs {
|
||||
if name == &name!(self) {
|
||||
if *name == name!(self) {
|
||||
cov_mark::hit!(extern_crate_self_as);
|
||||
let root = match self.def_map.block {
|
||||
Some(_) => {
|
||||
@ -1105,7 +1105,7 @@ impl DefCollector<'_> {
|
||||
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,
|
||||
def_collector: self,
|
||||
macro_depth: directive.depth,
|
||||
module_id: directive.module_id,
|
||||
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() {
|
||||
return true;
|
||||
}
|
||||
@ -1138,7 +1197,11 @@ impl DefCollector<'_> {
|
||||
|
||||
// Skip #[test]/#[bench] expansion, which would merely result in more memory usage
|
||||
// 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 item_tree = self.db.file_item_tree(file_id);
|
||||
let mod_dir = self.mod_dirs[&directive.module_id].clone();
|
||||
@ -1281,7 +1344,7 @@ impl DefCollector<'_> {
|
||||
for directive in &self.unresolved_macros {
|
||||
match &directive.kind {
|
||||
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,
|
||||
*expand_to,
|
||||
self.db,
|
||||
@ -1297,15 +1360,13 @@ impl DefCollector<'_> {
|
||||
resolved_res.resolved_def.take_macros()
|
||||
},
|
||||
&mut |_| (),
|
||||
) {
|
||||
Ok(_) => (),
|
||||
Err(UnresolvedMacro { path }) => {
|
||||
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
|
||||
directive.module_id,
|
||||
ast_id.ast_id,
|
||||
path,
|
||||
));
|
||||
}
|
||||
);
|
||||
if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
|
||||
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
|
||||
directive.module_id,
|
||||
ast_id.ast_id,
|
||||
path,
|
||||
));
|
||||
}
|
||||
}
|
||||
MacroDirectiveKind::Derive { .. } | MacroDirectiveKind::Attr { .. } => {
|
||||
@ -1747,26 +1808,23 @@ impl ModCollector<'_, '_> {
|
||||
});
|
||||
|
||||
for attr in iter {
|
||||
if attr.path.as_ident() == Some(&hir_expand::name![derive]) {
|
||||
self.collect_derive(attr, mod_item);
|
||||
} else if self.is_builtin_or_registered_attr(&attr.path) {
|
||||
if self.is_builtin_or_registered_attr(&attr.path) {
|
||||
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(())
|
||||
@ -1800,36 +1858,6 @@ impl ModCollector<'_, '_> {
|
||||
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.
|
||||
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
|
||||
|
@ -669,19 +669,20 @@ pub struct bar;
|
||||
fn expand_derive() {
|
||||
let map = compute_crate_def_map(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
use core::Copy;
|
||||
//- /main.rs crate:main deps:core
|
||||
use core::Copy;
|
||||
|
||||
#[derive(Copy, core::Clone)]
|
||||
struct Foo;
|
||||
#[core::derive(Copy, core::Clone)]
|
||||
struct Foo;
|
||||
|
||||
//- /core.rs crate:core
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Clone {}
|
||||
"#,
|
||||
//- /core.rs crate:core
|
||||
#[rustc_builtin_macro]
|
||||
pub macro derive($item:item) {}
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Clone {}
|
||||
"#,
|
||||
);
|
||||
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);
|
||||
let map = compute_crate_def_map(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
use core::Clone;
|
||||
//- /main.rs crate:main deps:core
|
||||
use core::{Clone, derive};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[unresolved]
|
||||
struct Foo;
|
||||
#[derive(Clone)]
|
||||
#[unresolved]
|
||||
struct Foo;
|
||||
|
||||
//- /core.rs crate:core
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Clone {}
|
||||
"#,
|
||||
//- /core.rs crate:core
|
||||
#[rustc_builtin_macro]
|
||||
pub macro derive($item:item) {}
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Clone {}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(map.modules[map.root].scope.impls().len(), 1);
|
||||
}
|
||||
@ -799,6 +802,9 @@ fn resolves_derive_helper() {
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:proc
|
||||
#[rustc_builtin_macro]
|
||||
pub macro derive($item:item) {}
|
||||
|
||||
#[derive(proc::Derive)]
|
||||
#[helper]
|
||||
#[unresolved]
|
||||
@ -811,6 +817,7 @@ fn derive() {}
|
||||
expect![[r#"
|
||||
crate
|
||||
S: t v
|
||||
derive: m
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -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! {
|
||||
(bench, Bench) => dummy_attr_expand,
|
||||
(cfg_accessible, CfgAccessible) => dummy_attr_expand,
|
||||
@ -46,16 +58,6 @@ register_builtin! {
|
||||
(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(
|
||||
ident: &name::Name,
|
||||
krate: CrateId,
|
||||
|
@ -974,61 +974,12 @@ fn infer_builtin_macros_env() {
|
||||
fn infer_derive_clone_simple() {
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
//- minicore: derive, clone
|
||||
#[derive(Clone)]
|
||||
struct S;
|
||||
fn test() {
|
||||
S.clone();
|
||||
} //^^^^^^^^^ 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() {
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
//- minicore: clone, derive
|
||||
#[derive(Clone)]
|
||||
struct S;
|
||||
#[derive(Clone)]
|
||||
@ -1048,21 +999,6 @@ fn test() {
|
||||
x;
|
||||
//^ (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
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main
|
||||
//- minicore: derive
|
||||
use foo::Foo;
|
||||
|
||||
#[derive(Foo)]
|
||||
|
@ -367,9 +367,7 @@ fn main() {
|
||||
check(
|
||||
r#"
|
||||
//- proc_macros: identity
|
||||
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Clone {}
|
||||
//- minicore: clone, derive
|
||||
|
||||
#[proc_macros::identity]
|
||||
#[derive(C$0lone)]
|
||||
@ -377,7 +375,7 @@ struct Foo {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Clone
|
||||
impl< >crate::clone::Clone for Foo< >{}
|
||||
impl< >core::clone::Clone for Foo< >{}
|
||||
|
||||
"#]],
|
||||
);
|
||||
@ -387,10 +385,7 @@ struct Foo {}
|
||||
fn macro_expand_derive2() {
|
||||
check(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Clone {}
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
//- minicore: copy, clone, derive
|
||||
|
||||
#[derive(Cop$0y)]
|
||||
#[derive(Clone)]
|
||||
@ -398,7 +393,7 @@ struct Foo {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Copy
|
||||
impl< >crate::marker::Copy for Foo< >{}
|
||||
impl< >core::marker::Copy for Foo< >{}
|
||||
|
||||
"#]],
|
||||
);
|
||||
@ -408,19 +403,16 @@ struct Foo {}
|
||||
fn macro_expand_derive_multi() {
|
||||
check(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Clone {}
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
//- minicore: copy, clone, derive
|
||||
|
||||
#[derive(Cop$0y, Clone)]
|
||||
struct Foo {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
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< >{}
|
||||
|
||||
"#]],
|
||||
);
|
||||
|
@ -805,6 +805,9 @@ bar = {path = "../bar"}
|
||||
|
||||
//- /foo/src/main.rs
|
||||
use bar::Bar;
|
||||
|
||||
#[rustc_builtin_macro]
|
||||
macro derive($item:item) {}
|
||||
trait Bar {
|
||||
fn bar();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user