-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
macros: fix inert attributes from proc_macro_derives
with #![feature(proc_macro)]
#39572
Conversation
An alternative would be for |
cc #38356 |
e0e1eb0
to
1068ff0
Compare
proc_macro_derives
behind #![feature(proc_macro)]
proc_macro_derives
with #![feature(proc_macro)]
1094f9f
to
b260146
Compare
); | ||
let attrs = attributes.iter().cloned().map(Symbol::intern).collect::<Vec<_>>(); | ||
let derive = ProcMacroDerive::new(expand, attrs.clone()); | ||
let derive = SyntaxExtension::ProcMacroDerive(Box::new(derive), attrs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason you can't just store the ProcMacroDerive struct without boxing? Seems a waste to have two separate Vecs for the same thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could, but it would require moving more code from libsyntax_ext
into libsyntax
. Still might be worth it, but probably best for another PR (perf. impact is negligible).
src/librustc_resolve/macros.rs
Outdated
@@ -172,7 +176,8 @@ impl<'a> base::Resolver for Resolver<'a> { | |||
ImportResolver { resolver: self }.resolve_imports() | |||
} | |||
|
|||
fn find_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> { | |||
fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What makes this legacy
, will it only resolve non-path attributes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It only resolves legacy attribute macro invocations and legacy derive invocations (i.e. attributes and derives from #![plugin(...)]
). I'll add a comment.
src/librustc_resolve/macros.rs
Outdated
} else { | ||
attrs[i].value = ast::MetaItem { | ||
name: attrs[i].name(), | ||
span: span, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be the span for the old derive attribute?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, good point -- will do.
let span = traits.remove(j).span; | ||
self.gate_legacy_custom_derive(legacy_name, span); | ||
if traits.is_empty() { | ||
attrs.remove(i); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this attribute be marked as used, or does that happen elsewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It gets marked as used during expansion (here).
src/libsyntax/ext/expand.rs
Outdated
let expansion = match ext { | ||
Some(ext) => self.expand_invoc(invoc, ext), | ||
None => invoc.expansion_kind.dummy(invoc.span()), | ||
let (expansion, new_invocations) = if let Some(ext) = ext { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems a bit complex, should there be a separate expand_derives function? (Two Some(ext)
's?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably -- I was thinking maybe a separate collect_derives
. I was thinking once we implement inert attribute vs macro attribute ambiguity errors (i.e. scope (b) from #39347 (comment)) and expanding items before derives, we'll have a better idea of how best to abstract / refactor out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, makes sense. I think it still deserves a comment for now at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
src/libsyntax/ext/expand.rs
Outdated
return attrs; | ||
} | ||
|
||
traits = collect_derives(&mut self.cx, &attrs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there are multiple inert attributes, will collect_derives
be called multiple times? If so, multiple derive warnings may occur.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No -- we don't call classify_item
again after we collect a potential proc_macro_attribute
invocation.
If the potential proc_macro_attribute
invocation ends up being an inert attribute, we just mutate the invocation and re-resolve it (separate from this code-path).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I quite understand. What's the final for
loop needed for?
_ => {} | ||
} | ||
|
||
for &(name, span) in traits { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand what's happening here. For each InvocKind::Attr, we have a possible instance of an inert attribute, then we loop through the traits seeing if it belongs to any invocation? And if it does, we simply add it to a dummy item, and mark it as used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah -- if it is an inert attribute, we mark it as known (so that it won't get collected as a proc_macro_attribute
invocation again), add it back to underlying item (the dummy item is just a workaround for lack of easy mutable access to an Expansion
's attributes), and put the invocation back on the work list.
b260146
to
51e7f21
Compare
cc @alexcrichton I feel like this discussion has come up before in the macros 1.1 process. |
Yeah, c.f. #35900 (comment) and #37067. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r=me with the added comment, but I'd like to give Alex a chance to see this before merging
src/libsyntax/ext/base.rs
Outdated
@@ -514,7 +514,7 @@ pub enum SyntaxExtension { | |||
/// The input is the annotated item. | |||
/// Allows generating code to implement a Trait for a given struct | |||
/// or enum item. | |||
ProcMacroDerive(Box<MultiItemModifier>), | |||
ProcMacroDerive(Box<MultiItemModifier>, Vec<Symbol>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you comment on the additional arg please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
IIRC, #37067 was written when |
Yeah I think that the current behavior made more sense when custom derive could alter the item itself, but nowadays where it can't then this looks good to me! (I can't think of any problems at least) |
51e7f21
to
1029ee0
Compare
@bors r=nrc |
📌 Commit 1029ee0 has been approved by |
Did you fix the span issue in |
⌛ Testing commit 1029ee0 with merge c110a97... |
…re(proc_macro)]`.
1029ee0
to
2cc61ee
Compare
@keeperofdakeys Yeah. |
📌 Commit 2cc61ee has been approved by |
@bors p=1 |
macros: fix inert attributes from `proc_macro_derives` with `#![feature(proc_macro)]` This PR refactors collection of `proc_macro_derive` invocations to fix #39347. After this PR, the input to a `#[proc_macro_derive]` function no longer sees `#[derive]`s on the underlying item. For example, consider: ```rust extern crate my_derives; use my_derives::{Trait, Trait2}; #[derive(Copy, Clone)] #[derive(Trait)] #[derive(Trait2)] struct S; ``` Today, the input to the `Trait` derive is `#[derive(Copy, Clone, Trait2)] struct S;`, and the input to the `Trait2` derive is `#[derive(Copy, Clone)] struct S;`. More generally, a `proc_macro_derive` sees all builtin derives, as well as all `proc_macro_derive`s listed *after* the one being invoked. After this PR, both `Trait` and `Trait2` will see `struct S;`. This is a [breaking-change], but I believe it is highly unlikely to cause breakage in practice. r? @nrc
☀️ Test successful - status-appveyor, status-travis |
I'm tagging this as relnotes since the there is certainly a change in behavior here. |
…opy`" This reverts commit 4117fff. As of rust 1.17, proc-macros don't get attributes other than their own in the token stream they're fed. See rust-lang/rust#39572
…opy`" This reverts commit 4117fff. As of rust 1.17, proc-macros don't get attributes other than their own in the token stream they're fed. See rust-lang/rust#39572
…opy`" This reverts commit 4117fff. As of rust 1.17, proc-macros don't get attributes other than their own in the token stream they're fed. See rust-lang/rust#39572
This PR refactors collection of
proc_macro_derive
invocations to fix #39347.After this PR, the input to a
#[proc_macro_derive]
function no longer sees#[derive]
s on the underlying item. For example, consider:Today, the input to the
Trait
derive is#[derive(Copy, Clone, Trait2)] struct S;
, and the input to theTrait2
derive is#[derive(Copy, Clone)] struct S;
. More generally, aproc_macro_derive
sees all builtin derives, as well as allproc_macro_derive
s listed after the one being invoked.After this PR, both
Trait
andTrait2
will seestruct S;
.This is a [breaking-change], but I believe it is highly unlikely to cause breakage in practice.
r? @nrc