Implement span quoting for proc-macros
This PR implements span quoting, allowing proc-macros to produce spans pointing *into their own crate*. This is used by the unstable `proc_macro::quote!` macro, allowing us to get error messages like this: ``` error[E0412]: cannot find type `MissingType` in this scope --> $DIR/auxiliary/span-from-proc-macro.rs:37:20 | LL | pub fn error_from_attribute(_args: TokenStream, _input: TokenStream) -> TokenStream { | ----------------------------------------------------------------------------------- in this expansion of procedural macro `#[error_from_attribute]` ... LL | field: MissingType | ^^^^^^^^^^^ not found in this scope | ::: $DIR/span-from-proc-macro.rs:8:1 | LL | #[error_from_attribute] | ----------------------- in this macro invocation ``` Here, `MissingType` occurs inside the implementation of the proc-macro `#[error_from_attribute]`. Previosuly, this would always result in a span pointing at `#[error_from_attribute]` This will make many proc-macro-related error message much more useful - when a proc-macro generates code containing an error, users will get an error message pointing directly at that code (within the macro definition), instead of always getting a span pointing at the macro invocation site. This is implemented as follows: * When a proc-macro crate is being *compiled*, it causes the `quote!` macro to get run. This saves all of the sapns in the input to `quote!` into the metadata of *the proc-macro-crate* (which we are currently compiling). The `quote!` macro then expands to a call to `proc_macro::Span::recover_proc_macro_span(id)`, where `id` is an opaque identifier for the span in the crate metadata. * When the same proc-macro crate is *run* (e.g. it is loaded from disk and invoked by some consumer crate), the call to `proc_macro::Span::recover_proc_macro_span` causes us to load the span from the proc-macro crate's metadata. The proc-macro then produces a `TokenStream` containing a `Span` pointing into the proc-macro crate itself. The recursive nature of 'quote!' can be difficult to understand at first. The file `src/test/ui/proc-macro/quote-debug.stdout` shows the output of the `quote!` macro, which should make this eaier to understand. This PR also supports custom quoting spans in custom quote macros (e.g. the `quote` crate). All span quoting goes through the `proc_macro::quote_span` method, which can be called by a custom quote macro to perform span quoting. An example of this usage is provided in `src/test/ui/proc-macro/auxiliary/custom-quote.rs` Custom quoting currently has a few limitations: In order to quote a span, we need to generate a call to `proc_macro::Span::recover_proc_macro_span`. However, proc-macros support renaming the `proc_macro` crate, so we can't simply hardcode this path. Previously, the `quote_span` method used the path `crate::Span` - however, this only works when it is called by the builtin `quote!` macro in the same crate. To support being called from arbitrary crates, we need access to the name of the `proc_macro` crate to generate a path. This PR adds an additional argument to `quote_span` to specify the name of the `proc_macro` crate. Howver, this feels kind of hacky, and we may want to change this before stabilizing anything quote-related. Additionally, using `quote_span` currently requires enabling the `proc_macro_internals` feature. The builtin `quote!` macro has an `#[allow_internal_unstable]` attribute, but this won't work for custom quote implementations. This will likely require some additional tricks to apply `allow_internal_unstable` to the span of `proc_macro::Span::recover_proc_macro_span`.
This commit is contained in:
parent
ea3068efe4
commit
f916b0474a
34 changed files with 494 additions and 69 deletions
|
@ -716,30 +716,37 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
.decode((self, sess))
|
||||
}
|
||||
|
||||
fn load_proc_macro(&self, id: DefIndex, sess: &Session) -> SyntaxExtension {
|
||||
let (name, kind, helper_attrs) = match *self.raw_proc_macro(id) {
|
||||
fn load_proc_macro(&self, def_id: DefId, sess: &Session) -> SyntaxExtension {
|
||||
let (name, kind, helper_attrs) = match *self.raw_proc_macro(def_id.index) {
|
||||
ProcMacro::CustomDerive { trait_name, attributes, client } => {
|
||||
let helper_attrs =
|
||||
attributes.iter().cloned().map(Symbol::intern).collect::<Vec<_>>();
|
||||
(
|
||||
trait_name,
|
||||
SyntaxExtensionKind::Derive(Box::new(ProcMacroDerive { client })),
|
||||
SyntaxExtensionKind::Derive(Box::new(ProcMacroDerive {
|
||||
client,
|
||||
krate: def_id.krate,
|
||||
})),
|
||||
helper_attrs,
|
||||
)
|
||||
}
|
||||
ProcMacro::Attr { name, client } => {
|
||||
(name, SyntaxExtensionKind::Attr(Box::new(AttrProcMacro { client })), Vec::new())
|
||||
}
|
||||
ProcMacro::Bang { name, client } => {
|
||||
(name, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client })), Vec::new())
|
||||
}
|
||||
ProcMacro::Attr { name, client } => (
|
||||
name,
|
||||
SyntaxExtensionKind::Attr(Box::new(AttrProcMacro { client, krate: def_id.krate })),
|
||||
Vec::new(),
|
||||
),
|
||||
ProcMacro::Bang { name, client } => (
|
||||
name,
|
||||
SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client, krate: def_id.krate })),
|
||||
Vec::new(),
|
||||
),
|
||||
};
|
||||
|
||||
let attrs: Vec<_> = self.get_item_attrs(id, sess).collect();
|
||||
let attrs: Vec<_> = self.get_item_attrs(def_id.index, sess).collect();
|
||||
SyntaxExtension::new(
|
||||
sess,
|
||||
kind,
|
||||
self.get_span(id, sess),
|
||||
self.get_span(def_id.index, sess),
|
||||
helper_attrs,
|
||||
self.root.edition,
|
||||
Symbol::intern(name),
|
||||
|
@ -1379,6 +1386,15 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_proc_macro_quoted_span(&self, index: usize, sess: &Session) -> Span {
|
||||
self.root
|
||||
.tables
|
||||
.proc_macro_quoted_spans
|
||||
.get(self, index)
|
||||
.unwrap_or_else(|| panic!("Missing proc macro quoted span: {:?}", index))
|
||||
.decode((self, sess))
|
||||
}
|
||||
|
||||
fn get_foreign_modules(&self, tcx: TyCtxt<'tcx>) -> Lrc<FxHashMap<DefId, ForeignModule>> {
|
||||
if self.root.is_proc_macro_crate() {
|
||||
// Proc macro crates do not have any *target* foreign modules.
|
||||
|
|
|
@ -411,7 +411,7 @@ impl CStore {
|
|||
|
||||
let data = self.get_crate_data(id.krate);
|
||||
if data.root.is_proc_macro_crate() {
|
||||
return LoadedMacro::ProcMacro(data.load_proc_macro(id.index, sess));
|
||||
return LoadedMacro::ProcMacro(data.load_proc_macro(id, sess));
|
||||
}
|
||||
|
||||
let span = data.get_span(id.index, sess);
|
||||
|
@ -461,6 +461,15 @@ impl CStore {
|
|||
pub fn item_attrs(&self, def_id: DefId, sess: &Session) -> Vec<ast::Attribute> {
|
||||
self.get_crate_data(def_id.krate).get_item_attrs(def_id.index, sess).collect()
|
||||
}
|
||||
|
||||
pub fn get_proc_macro_quoted_span_untracked(
|
||||
&self,
|
||||
cnum: CrateNum,
|
||||
id: usize,
|
||||
sess: &Session,
|
||||
) -> Span {
|
||||
self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess)
|
||||
}
|
||||
}
|
||||
|
||||
impl CrateStore for CStore {
|
||||
|
|
|
@ -1579,6 +1579,11 @@ impl EncodeContext<'a, 'tcx> {
|
|||
let proc_macro_decls_static = tcx.proc_macro_decls_static(LOCAL_CRATE).unwrap().index;
|
||||
let stability = tcx.lookup_stability(DefId::local(CRATE_DEF_INDEX)).copied();
|
||||
let macros = self.lazy(hir.krate().proc_macros.iter().map(|p| p.owner.local_def_index));
|
||||
let spans = self.tcx.sess.parse_sess.proc_macro_quoted_spans();
|
||||
for (i, span) in spans.into_iter().enumerate() {
|
||||
let span = self.lazy(span);
|
||||
self.tables.proc_macro_quoted_spans.set(i, span);
|
||||
}
|
||||
|
||||
record!(self.tables.def_kind[LOCAL_CRATE.as_def_id()] <- DefKind::Mod);
|
||||
record!(self.tables.span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id()));
|
||||
|
|
|
@ -258,15 +258,15 @@ crate struct TraitImpls {
|
|||
|
||||
/// Define `LazyTables` and `TableBuilders` at the same time.
|
||||
macro_rules! define_tables {
|
||||
($($name:ident: Table<DefIndex, $T:ty>),+ $(,)?) => {
|
||||
($($name:ident: Table<$IDX:ty, $T:ty>),+ $(,)?) => {
|
||||
#[derive(MetadataEncodable, MetadataDecodable)]
|
||||
crate struct LazyTables<'tcx> {
|
||||
$($name: Lazy!(Table<DefIndex, $T>)),+
|
||||
$($name: Lazy!(Table<$IDX, $T>)),+
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TableBuilders<'tcx> {
|
||||
$($name: TableBuilder<DefIndex, $T>),+
|
||||
$($name: TableBuilder<$IDX, $T>),+
|
||||
}
|
||||
|
||||
impl TableBuilders<'tcx> {
|
||||
|
@ -315,6 +315,7 @@ define_tables! {
|
|||
// definitions from any given crate.
|
||||
def_keys: Table<DefIndex, Lazy<DefKey>>,
|
||||
def_path_hashes: Table<DefIndex, Lazy<DefPathHash>>,
|
||||
proc_macro_quoted_spans: Table<usize, Lazy<Span>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, MetadataEncodable, MetadataDecodable)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue