Skip to content

Commit

Permalink
resolve: Make proc macro stubs less stubby
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
petrochenkov committed Jul 10, 2019
1 parent b392781 commit cd0fd63
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 75 deletions.
18 changes: 1 addition & 17 deletions src/librustc_resolve/build_reduced_graph.rs
Expand Up @@ -46,20 +46,6 @@ use log::debug;

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 {
Expand Down Expand Up @@ -470,9 +456,7 @@ impl<'a> Resolver<'a> {

// 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.
Expand Down
21 changes: 17 additions & 4 deletions src/librustc_resolve/lib.rs
Expand Up @@ -40,7 +40,7 @@ use rustc_metadata::cstore::CStore;
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};
Expand Down Expand Up @@ -1663,10 +1663,13 @@ pub struct Resolver<'a> {
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>>,
Expand Down Expand Up @@ -1925,9 +1928,8 @@ impl<'a> Resolver<'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,
Expand Down Expand Up @@ -2002,6 +2004,8 @@ impl<'a> Resolver<'a> {
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,
Expand All @@ -2010,6 +2014,7 @@ impl<'a> Resolver<'a> {
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:
Expand All @@ -2027,6 +2032,14 @@ impl<'a> Resolver<'a> {
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);
Expand Down
76 changes: 52 additions & 24 deletions src/librustc_resolve/macros.rs
Expand Up @@ -11,7 +11,7 @@ use rustc::hir::def::{self, DefKind, NonMacroAttrKind};
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};
Expand Down Expand Up @@ -127,6 +127,21 @@ fn fast_print_path(path: &ast::Path) -> Symbol {
}
}

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()
Expand Down Expand Up @@ -216,10 +231,9 @@ impl<'a> base::Resolver for Resolver<'a> {
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();
Expand Down Expand Up @@ -305,13 +319,14 @@ impl<'a> Resolver<'a> {
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 {
Expand Down Expand Up @@ -1100,19 +1115,32 @@ impl<'a> Resolver<'a> {
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);
Expand All @@ -1121,7 +1149,7 @@ impl<'a> Resolver<'a> {
} 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
Expand All @@ -1131,18 +1159,18 @@ impl<'a> Resolver<'a> {
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));
}
}
}
22 changes: 21 additions & 1 deletion src/libsyntax/ext/base.rs
Expand Up @@ -10,7 +10,7 @@ use crate::parse::token;
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};
Expand Down Expand Up @@ -640,6 +640,26 @@ impl SyntaxExtension {
}
}

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,
Expand Down
10 changes: 6 additions & 4 deletions src/test/ui/proc-macro/macro-namespace-reserved-2.rs
Expand Up @@ -25,22 +25,24 @@ fn check_bang1() {
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;
66 changes: 41 additions & 25 deletions src/test/ui/proc-macro/macro-namespace-reserved-2.stderr
Expand Up @@ -4,24 +4,6 @@ error: can't use a procedural macro from the same crate that defines it
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
|
Expand All @@ -34,23 +16,57 @@ error: can't use a procedural macro from the same crate that defines it
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`.

0 comments on commit cd0fd63

Please sign in to comment.