Create real working and registered (even if dummy) `SyntaxExtension`s for them.
This improves error recovery and allows to avoid all special cases for proc macro stubs (except for the error on use, of course).
The introduced dummy `SyntaxExtension`s can be used for any other inappropriately resolved macros as well.
type Res = def::Res<NodeId>;
-fn proc_macro_stub(item: &Item) -> Option<(Ident, Span)> {
- if attr::contains_name(&item.attrs, sym::proc_macro) ||
- attr::contains_name(&item.attrs, sym::proc_macro_attribute) {
- return Some((item.ident, item.span));
- } else if let Some(attr) = attr::find_by_name(&item.attrs, sym::proc_macro_derive) {
- if let Some(nested_meta) = attr.meta_item_list().and_then(|list| list.get(0).cloned()) {
- if let Some(ident) = nested_meta.ident() {
- return Some((ident, ident.span));
- }
- }
- }
- None
-}
-
impl<'a> ToNameBinding<'a> for (Module<'a>, ty::Visibility, Span, Mark) {
fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> {
arenas.alloc_name_binding(NameBinding {
// Functions introducing procedural macros reserve a slot
// in the macro namespace as well (see #52225).
- if let Some((ident, span)) = proc_macro_stub(item) {
- self.define(parent, ident, MacroNS, (res, vis, span, expansion));
- }
+ self.define_macro(item, expansion, &mut LegacyScope::Empty);
}
// These items live in the type namespace.
use syntax::source_map::SourceMap;
use syntax::ext::hygiene::{Mark, Transparency, SyntaxContext};
use syntax::ast::{self, Name, NodeId, Ident, FloatTy, IntTy, UintTy};
-use syntax::ext::base::{SyntaxExtension, SyntaxExtensionKind};
+use syntax::ext::base::SyntaxExtension;
use syntax::ext::base::Determinacy::{self, Determined, Undetermined};
use syntax::ext::base::MacroKind;
use syntax::symbol::{Symbol, kw, sym};
macro_use_prelude: FxHashMap<Name, &'a NameBinding<'a>>,
pub all_macros: FxHashMap<Name, Res>,
macro_map: FxHashMap<DefId, Lrc<SyntaxExtension>>,
+ dummy_ext_bang: Lrc<SyntaxExtension>,
+ dummy_ext_derive: Lrc<SyntaxExtension>,
non_macro_attrs: [Lrc<SyntaxExtension>; 2],
macro_defs: FxHashMap<Mark, DefId>,
local_macro_def_scopes: FxHashMap<NodeId, Module<'a>>,
unused_macros: NodeMap<Span>,
+ proc_macro_stubs: NodeSet,
/// Maps the `Mark` of an expansion to its containing module or block.
invocations: FxHashMap<Mark, &'a InvocationData<'a>>,
macro_defs.insert(Mark::root(), root_def_id);
let features = session.features_untracked();
- let non_macro_attr = |mark_used| Lrc::new(SyntaxExtension::default(
- SyntaxExtensionKind::NonMacroAttr { mark_used }, session.edition()
- ));
+ let non_macro_attr =
+ |mark_used| Lrc::new(SyntaxExtension::non_macro_attr(mark_used, session.edition()));
Resolver {
session,
macro_use_prelude: FxHashMap::default(),
all_macros: FxHashMap::default(),
macro_map: FxHashMap::default(),
+ dummy_ext_bang: Lrc::new(SyntaxExtension::dummy_bang(session.edition())),
+ dummy_ext_derive: Lrc::new(SyntaxExtension::dummy_derive(session.edition())),
non_macro_attrs: [non_macro_attr(false), non_macro_attr(true)],
invocations,
macro_defs,
potentially_unused_imports: Vec::new(),
struct_constructors: Default::default(),
unused_macros: Default::default(),
+ proc_macro_stubs: Default::default(),
current_type_ascription: Vec::new(),
injected_crate: None,
active_features:
self.non_macro_attrs[mark_used as usize].clone()
}
+ fn dummy_ext(&self, macro_kind: MacroKind) -> Lrc<SyntaxExtension> {
+ match macro_kind {
+ MacroKind::Bang => self.dummy_ext_bang.clone(),
+ MacroKind::Derive => self.dummy_ext_derive.clone(),
+ MacroKind::Attr => self.non_macro_attr(true),
+ }
+ }
+
/// Runs the function on each namespace.
fn per_ns<F: FnMut(&mut Self, Namespace)>(&mut self, mut f: F) {
f(self, TypeNS);
use rustc::hir::map::DefCollector;
use rustc::middle::stability;
use rustc::{ty, lint, span_bug};
-use syntax::ast::{self, Ident};
+use syntax::ast::{self, Ident, ItemKind};
use syntax::attr::{self, StabilityLevel};
use syntax::errors::DiagnosticBuilder;
use syntax::ext::base::{self, Determinacy};
}
}
+fn proc_macro_stub(item: &ast::Item) -> Option<(MacroKind, Ident, Span)> {
+ if attr::contains_name(&item.attrs, sym::proc_macro) {
+ return Some((MacroKind::Bang, item.ident, item.span));
+ } else if attr::contains_name(&item.attrs, sym::proc_macro_attribute) {
+ return Some((MacroKind::Attr, item.ident, item.span));
+ } else if let Some(attr) = attr::find_by_name(&item.attrs, sym::proc_macro_derive) {
+ if let Some(nested_meta) = attr.meta_item_list().and_then(|list| list.get(0).cloned()) {
+ if let Some(ident) = nested_meta.ident() {
+ return Some((MacroKind::Derive, ident, ident.span));
+ }
+ }
+ }
+ None
+}
+
impl<'a> base::Resolver for Resolver<'a> {
fn next_node_id(&mut self) -> ast::NodeId {
self.session.next_node_id()
let parent_scope = self.invoc_parent_scope(invoc_id, derives_in_scope);
let (res, ext) = match self.resolve_macro_to_res(path, kind, &parent_scope, true, force) {
Ok((res, ext)) => (res, ext),
- // Replace unresolved attributes with used inert attributes for better recovery.
- Err(Determinacy::Determined) if kind == MacroKind::Attr =>
- (Res::Err, self.non_macro_attr(true)),
- Err(determinacy) => return Err(determinacy),
+ // Return dummy syntax extensions for unresolved macros for better recovery.
+ Err(Determinacy::Determined) => (Res::Err, self.dummy_ext(kind)),
+ Err(Determinacy::Undetermined) => return Err(Determinacy::Undetermined),
};
let span = invoc.span();
Res::Def(DefKind::Macro(_), def_id) => {
if let Some(node_id) = self.definitions.as_local_node_id(def_id) {
self.unused_macros.remove(&node_id);
+ if self.proc_macro_stubs.contains(&node_id) {
+ self.session.span_err(
+ path.span,
+ "can't use a procedural macro from the same crate that defines it",
+ );
+ }
}
}
- Res::Def(DefKind::Fn, _) => {
- let msg = "can't use a procedural macro from the same crate that defines it";
- self.session.span_err(path.span, msg);
- return Err(Determinacy::Determined);
- }
Res::NonMacroAttr(attr_kind) => {
if kind == MacroKind::Attr {
if attr_kind == NonMacroAttrKind::Custom {
item: &ast::Item,
expansion: Mark,
current_legacy_scope: &mut LegacyScope<'a>) {
- self.local_macro_def_scopes.insert(item.id, self.current_module);
- let ident = item.ident;
+ let (ext, ident, span, is_legacy) = match &item.node {
+ ItemKind::MacroDef(def) => {
+ let ext = Lrc::new(macro_rules::compile(
+ &self.session.parse_sess,
+ &self.session.features_untracked(),
+ item,
+ self.session.edition(),
+ ));
+ (ext, item.ident, item.span, def.legacy)
+ }
+ ItemKind::Fn(..) => match proc_macro_stub(item) {
+ Some((macro_kind, ident, span)) => {
+ self.proc_macro_stubs.insert(item.id);
+ (self.dummy_ext(macro_kind), ident, span, false)
+ }
+ None => return,
+ }
+ _ => unreachable!(),
+ };
let def_id = self.definitions.local_def_id(item.id);
- let ext = Lrc::new(macro_rules::compile(&self.session.parse_sess,
- &self.session.features_untracked(),
- item, self.session.edition()));
- let macro_kind = ext.macro_kind();
- let res = Res::Def(DefKind::Macro(macro_kind), def_id);
+ let res = Res::Def(DefKind::Macro(ext.macro_kind()), def_id);
self.macro_map.insert(def_id, ext);
+ self.local_macro_def_scopes.insert(item.id, self.current_module);
- let def = match item.node { ast::ItemKind::MacroDef(ref def) => def, _ => unreachable!() };
- if def.legacy {
+ if is_legacy {
let ident = ident.modern();
self.macro_names.insert(ident);
let is_macro_export = attr::contains_name(&item.attrs, sym::macro_export);
} else {
ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX))
};
- let binding = (res, vis, item.span, expansion).to_name_binding(self.arenas);
+ let binding = (res, vis, span, expansion).to_name_binding(self.arenas);
self.set_binding_parent_module(binding, self.current_module);
let legacy_binding = self.arenas.alloc_legacy_binding(LegacyBinding {
parent_legacy_scope: *current_legacy_scope, binding, ident
if is_macro_export {
let module = self.graph_root;
self.define(module, ident, MacroNS,
- (res, vis, item.span, expansion, IsMacroExport));
+ (res, vis, span, expansion, IsMacroExport));
} else {
self.check_reserved_macro_name(ident, res);
- self.unused_macros.insert(item.id, item.span);
+ self.unused_macros.insert(item.id, span);
}
} else {
let module = self.current_module;
let vis = self.resolve_visibility(&item.vis);
if vis != ty::Visibility::Public {
- self.unused_macros.insert(item.id, item.span);
+ self.unused_macros.insert(item.id, span);
}
- self.define(module, ident, MacroNS, (res, vis, item.span, expansion));
+ self.define(module, ident, MacroNS, (res, vis, span, expansion));
}
}
}
use crate::ptr::P;
use crate::symbol::{kw, sym, Ident, Symbol};
use crate::{ThinVec, MACRO_ARGUMENTS};
-use crate::tokenstream::{self, TokenStream};
+use crate::tokenstream::{self, TokenStream, TokenTree};
use errors::{DiagnosticBuilder, DiagnosticId};
use smallvec::{smallvec, SmallVec};
}
}
+ pub fn dummy_bang(edition: Edition) -> SyntaxExtension {
+ fn expander<'cx>(_: &'cx mut ExtCtxt<'_>, span: Span, _: &[TokenTree])
+ -> Box<dyn MacResult + 'cx> {
+ DummyResult::any(span)
+ }
+ SyntaxExtension::default(SyntaxExtensionKind::LegacyBang(Box::new(expander)), edition)
+ }
+
+ pub fn dummy_derive(edition: Edition) -> SyntaxExtension {
+ fn expander(_: &mut ExtCtxt<'_>, _: Span, _: &ast::MetaItem, _: Annotatable)
+ -> Vec<Annotatable> {
+ Vec::new()
+ }
+ SyntaxExtension::default(SyntaxExtensionKind::Derive(Box::new(expander)), edition)
+ }
+
+ pub fn non_macro_attr(mark_used: bool, edition: Edition) -> SyntaxExtension {
+ SyntaxExtension::default(SyntaxExtensionKind::NonMacroAttr { mark_used }, edition)
+ }
+
pub fn expn_info(&self, call_site: Span, descr: Symbol) -> ExpnInfo {
ExpnInfo {
call_site,
my_macro!(); //~ ERROR can't use a procedural macro from the same crate that defines it
}
fn check_bang2() {
- my_macro_attr!(); //~ ERROR can't use a procedural macro from the same crate that defines it
+ my_macro_attr!(); //~ ERROR cannot find macro `my_macro_attr!` in this scope
}
fn check_bang3() {
- MyTrait!(); //~ ERROR can't use a procedural macro from the same crate that defines it
+ MyTrait!(); //~ ERROR cannot find macro `MyTrait!` in this scope
}
-#[my_macro] //~ ERROR can't use a procedural macro from the same crate that defines it
+#[my_macro] //~ ERROR attribute `my_macro` is currently unknown
fn check_attr1() {}
#[my_macro_attr] //~ ERROR can't use a procedural macro from the same crate that defines it
fn check_attr2() {}
#[MyTrait] //~ ERROR can't use a procedural macro from the same crate that defines it
+ //~| ERROR `MyTrait` is a derive macro
fn check_attr3() {}
-#[derive(my_macro)] //~ ERROR can't use a procedural macro from the same crate that defines it
+#[derive(my_macro)] //~ ERROR cannot find derive macro `my_macro` in this scope
struct CheckDerive1;
#[derive(my_macro_attr)] //~ ERROR can't use a procedural macro from the same crate that defines it
+ //~| ERROR macro `my_macro_attr` may not be used for derive attributes
struct CheckDerive2;
#[derive(MyTrait)] //~ ERROR can't use a procedural macro from the same crate that defines it
struct CheckDerive3;
LL | my_macro!();
| ^^^^^^^^
-error: can't use a procedural macro from the same crate that defines it
- --> $DIR/macro-namespace-reserved-2.rs:28:5
- |
-LL | my_macro_attr!();
- | ^^^^^^^^^^^^^
-
-error: can't use a procedural macro from the same crate that defines it
- --> $DIR/macro-namespace-reserved-2.rs:31:5
- |
-LL | MyTrait!();
- | ^^^^^^^
-
-error: can't use a procedural macro from the same crate that defines it
- --> $DIR/macro-namespace-reserved-2.rs:34:3
- |
-LL | #[my_macro]
- | ^^^^^^^^
-
error: can't use a procedural macro from the same crate that defines it
--> $DIR/macro-namespace-reserved-2.rs:36:3
|
LL | #[MyTrait]
| ^^^^^^^
-error: can't use a procedural macro from the same crate that defines it
- --> $DIR/macro-namespace-reserved-2.rs:41:10
+error: `MyTrait` is a derive macro
+ --> $DIR/macro-namespace-reserved-2.rs:38:1
|
-LL | #[derive(my_macro)]
- | ^^^^^^^^
+LL | #[MyTrait]
+ | ^^^^^^^^^^
error: can't use a procedural macro from the same crate that defines it
- --> $DIR/macro-namespace-reserved-2.rs:43:10
+ --> $DIR/macro-namespace-reserved-2.rs:44:10
+ |
+LL | #[derive(my_macro_attr)]
+ | ^^^^^^^^^^^^^
+
+error: macro `my_macro_attr` may not be used for derive attributes
+ --> $DIR/macro-namespace-reserved-2.rs:44:10
|
LL | #[derive(my_macro_attr)]
| ^^^^^^^^^^^^^
error: can't use a procedural macro from the same crate that defines it
- --> $DIR/macro-namespace-reserved-2.rs:45:10
+ --> $DIR/macro-namespace-reserved-2.rs:47:10
|
LL | #[derive(MyTrait)]
| ^^^^^^^
-error: aborting due to 9 previous errors
+error[E0658]: The attribute `my_macro` is currently unknown to the compiler and may have meaning added to it in the future
+ --> $DIR/macro-namespace-reserved-2.rs:34:3
+ |
+LL | #[my_macro]
+ | ^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/29642
+ = help: add #![feature(custom_attribute)] to the crate attributes to enable
+
+error: cannot find derive macro `my_macro` in this scope
+ --> $DIR/macro-namespace-reserved-2.rs:42:10
+ |
+LL | #[derive(my_macro)]
+ | ^^^^^^^^
+
+error: cannot find macro `my_macro_attr!` in this scope
+ --> $DIR/macro-namespace-reserved-2.rs:28:5
+ |
+LL | my_macro_attr!();
+ | ^^^^^^^^^^^^^
+
+error: cannot find macro `MyTrait!` in this scope
+ --> $DIR/macro-namespace-reserved-2.rs:31:5
+ |
+LL | MyTrait!();
+ | ^^^^^^^
+
+error: aborting due to 11 previous errors
+For more information about this error, try `rustc --explain E0658`.