-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Warn on pointless #[derive] in more places #50092
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -821,7 +821,7 @@ impl Stmt { | |
|
||
pub fn is_item(&self) -> bool { | ||
match self.node { | ||
StmtKind::Local(_) => true, | ||
StmtKind::Item(_) => true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😆 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey, you missed it too. I don't wanna hear it 😛 |
||
_ => false, | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -143,7 +143,7 @@ impl ExpansionKind { | |
} | ||
|
||
fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>(self, items: I) -> Expansion { | ||
let items = items.into_iter(); | ||
let mut items = items.into_iter(); | ||
match self { | ||
ExpansionKind::Items => | ||
Expansion::Items(items.map(Annotatable::expect_item).collect()), | ||
|
@@ -153,7 +153,14 @@ impl ExpansionKind { | |
Expansion::TraitItems(items.map(Annotatable::expect_trait_item).collect()), | ||
ExpansionKind::ForeignItems => | ||
Expansion::ForeignItems(items.map(Annotatable::expect_foreign_item).collect()), | ||
_ => unreachable!(), | ||
ExpansionKind::Stmts => Expansion::Stmts(items.map(Annotatable::expect_stmt).collect()), | ||
ExpansionKind::Expr => Expansion::Expr( | ||
items.next().expect("expected exactly one expression").expect_expr() | ||
), | ||
ExpansionKind::OptExpr => | ||
Expansion::OptExpr(items.next().map(Annotatable::expect_expr)), | ||
ExpansionKind::Pat | ExpansionKind::Ty => | ||
panic!("patterns and types aren't annotatable"), | ||
} | ||
} | ||
} | ||
|
@@ -886,14 +893,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { | |
self.collect(kind, InvocationKind::Attr { attr, traits, item }) | ||
} | ||
|
||
// If `item` is an attr invocation, remove and return the macro attribute. | ||
/// If `item` is an attr invocation, remove and return the macro attribute and derive traits. | ||
fn classify_item<T>(&mut self, mut item: T) -> (Option<ast::Attribute>, Vec<Path>, T) | ||
where T: HasAttrs, | ||
{ | ||
let (mut attr, mut traits) = (None, Vec::new()); | ||
|
||
item = item.map_attrs(|mut attrs| { | ||
if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs) { | ||
if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs, | ||
true) { | ||
attr = Some(legacy_attr_invoc); | ||
return attrs; | ||
} | ||
|
@@ -908,6 +916,28 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { | |
(attr, traits, item) | ||
} | ||
|
||
/// Alternative of `classify_item()` that ignores `#[derive]` so invocations fallthrough | ||
/// to the unused-attributes lint (making it an error on statements and expressions | ||
/// is a breaking change) | ||
fn classify_nonitem<T: HasAttrs>(&mut self, mut item: T) -> (Option<ast::Attribute>, T) { | ||
let mut attr = None; | ||
|
||
item = item.map_attrs(|mut attrs| { | ||
if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs, | ||
false) { | ||
attr = Some(legacy_attr_invoc); | ||
return attrs; | ||
} | ||
|
||
if self.cx.ecfg.proc_macro_enabled() { | ||
attr = find_attr_invoc(&mut attrs); | ||
} | ||
attrs | ||
}); | ||
|
||
(attr, item) | ||
} | ||
|
||
fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> { | ||
self.cfg.configure(node) | ||
} | ||
|
@@ -918,6 +948,13 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { | |
let features = self.cx.ecfg.features.unwrap(); | ||
for attr in attrs.iter() { | ||
feature_gate::check_attribute(attr, self.cx.parse_sess, features); | ||
|
||
// macros are expanded before any lint passes so this warning has to be hardcoded | ||
if attr.path == "derive" { | ||
self.cx.struct_span_warn(attr.span, "`#[derive]` does nothing on macro invocations") | ||
.note("this may become a hard error in a future release") | ||
.emit(); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -938,15 +975,16 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { | |
let mut expr = self.cfg.configure_expr(expr).into_inner(); | ||
expr.node = self.cfg.configure_expr_kind(expr.node); | ||
|
||
let (attr, derives, expr) = self.classify_item(expr); | ||
// ignore derives so they remain unused | ||
let (attr, expr) = self.classify_nonitem(expr); | ||
|
||
if attr.is_some() || !derives.is_empty() { | ||
if attr.is_some() { | ||
// collect the invoc regardless of whether or not attributes are permitted here | ||
// expansion will eat the attribute so it won't error later | ||
attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); | ||
|
||
// ExpansionKind::Expr requires the macro to emit an expression | ||
return self.collect_attr(attr, derives, Annotatable::Expr(P(expr)), ExpansionKind::Expr) | ||
return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), ExpansionKind::Expr) | ||
.make_expr(); | ||
} | ||
|
||
|
@@ -962,12 +1000,13 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { | |
let mut expr = configure!(self, expr).into_inner(); | ||
expr.node = self.cfg.configure_expr_kind(expr.node); | ||
|
||
let (attr, derives, expr) = self.classify_item(expr); | ||
// ignore derives so they remain unused | ||
let (attr, expr) = self.classify_nonitem(expr); | ||
|
||
if attr.is_some() || !derives.is_empty() { | ||
if attr.is_some() { | ||
attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); | ||
|
||
return self.collect_attr(attr, derives, Annotatable::Expr(P(expr)), | ||
return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), | ||
ExpansionKind::OptExpr) | ||
.make_opt_expr(); | ||
} | ||
|
@@ -1001,7 +1040,14 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { | |
|
||
// we'll expand attributes on expressions separately | ||
if !stmt.is_expr() { | ||
let (attr, derives, stmt_) = self.classify_item(stmt); | ||
let (attr, derives, stmt_) = if stmt.is_item() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I could flatten these two conditionals plus the |
||
self.classify_item(stmt) | ||
} else { | ||
// ignore derives on non-item statements so it falls through | ||
// to the unused-attributes lint | ||
let (attr, stmt) = self.classify_nonitem(stmt); | ||
(attr, vec![], stmt) | ||
}; | ||
|
||
if attr.is_some() || !derives.is_empty() { | ||
return self.collect_attr(attr, derives, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
// compile-pass | ||
|
||
#![feature(stmt_expr_attributes)] | ||
#![warn(unused_attributes)] //~ NOTE lint level defined here | ||
|
||
fn foo<#[derive(Debug)] T>() { //~ WARN unused attribute | ||
match 0 { | ||
#[derive(Debug)] //~ WARN unused attribute | ||
_ => (), | ||
} | ||
} | ||
|
||
fn main() { | ||
// fold_stmt (Item) | ||
#[allow(dead_code)] | ||
#[derive(Debug)] // should not warn | ||
struct Foo; | ||
|
||
// fold_stmt (Mac) | ||
#[derive(Debug)] | ||
//~^ WARN `#[derive]` does nothing on macro invocations | ||
//~| NOTE this may become a hard error in a future release | ||
println!("Hello, world!"); | ||
|
||
// fold_stmt (Semi) | ||
#[derive(Debug)] //~ WARN unused attribute | ||
"Hello, world!"; | ||
|
||
// fold_stmt (Local) | ||
#[derive(Debug)] //~ WARN unused attribute | ||
let _ = "Hello, world!"; | ||
|
||
// fold_expr | ||
let _ = #[derive(Debug)] "Hello, world!"; | ||
//~^ WARN unused attribute | ||
|
||
let _ = [ | ||
// fold_opt_expr | ||
#[derive(Debug)] //~ WARN unused attribute | ||
"Hello, world!" | ||
]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add test cases for #50092 (comment) as well? fn f<#[derive(Debug)] T>() {
match 0 {
#[derive(Debug)]
_ => {}
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @petrochenkov There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, the more bugs are fixed the better, especially with one-line changes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
warning: `#[derive]` does nothing on macro invocations | ||
--> $DIR/issue-49934.rs:30:5 | ||
| | ||
LL | #[derive(Debug)] | ||
| ^^^^^^^^^^^^^^^^ | ||
| | ||
= note: this may become a hard error in a future release | ||
|
||
warning: unused attribute | ||
--> $DIR/issue-49934.rs:16:8 | ||
| | ||
LL | fn foo<#[derive(Debug)] T>() { //~ WARN unused attribute | ||
| ^^^^^^^^^^^^^^^^ | ||
| | ||
note: lint level defined here | ||
--> $DIR/issue-49934.rs:14:9 | ||
| | ||
LL | #![warn(unused_attributes)] //~ NOTE lint level defined here | ||
| ^^^^^^^^^^^^^^^^^ | ||
|
||
warning: unused attribute | ||
--> $DIR/issue-49934.rs:18:9 | ||
| | ||
LL | #[derive(Debug)] //~ WARN unused attribute | ||
| ^^^^^^^^^^^^^^^^ | ||
|
||
warning: unused attribute | ||
--> $DIR/issue-49934.rs:36:5 | ||
| | ||
LL | #[derive(Debug)] //~ WARN unused attribute | ||
| ^^^^^^^^^^^^^^^^ | ||
|
||
warning: unused attribute | ||
--> $DIR/issue-49934.rs:40:5 | ||
| | ||
LL | #[derive(Debug)] //~ WARN unused attribute | ||
| ^^^^^^^^^^^^^^^^ | ||
|
||
warning: unused attribute | ||
--> $DIR/issue-49934.rs:44:13 | ||
| | ||
LL | let _ = #[derive(Debug)] "Hello, world!"; | ||
| ^^^^^^^^^^^^^^^^ | ||
|
||
warning: unused attribute | ||
--> $DIR/issue-49934.rs:49:9 | ||
| | ||
LL | #[derive(Debug)] //~ WARN unused 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.
I would almost recommend an audit of the entire
intravisit
module to ensure that anywhere attributes can appear they're being visited. This missing line took me three hours to debug because I assumed it was something wrong with my code.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 looks like every type in
hir
that has anattrs
field is now being walked byintravisit
. I think I've already fixed the last of the discrepancies.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.
On a different note, this field and a couple others use
ThinVec
while theattrs
fields on all the other types useHirVec
. I'm not sure of the difference but I think it's just an alias anyway. However, I got an error when passing&local.attrs
here when it works fine for invocations withHirVec
.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 see,
HirVec
is effectivelyBox<[T]>
whileThinVec
isOption<Box<Vec<T>>>
(fat pointer vs thin pointer). UsingThinVec
in these cases may have been a deliberate choice to reduce memory consumption.