Skip to content

Commit 78bcd9b

Browse files
committed
Auto merge of #50092 - abonander:issue-49934, r=petrochenkov
Warn on pointless #[derive] in more places This fixes the regression in #49934 and ensures that unused `#[derive]` invocations on statements, expressions and generic type parameters survive to trip the `unused_attributes` lint. There is a separate warning hardcoded for `#[derive]` on macro invocations since linting (even the early-lint pass) occurs after expansion. This also adds regression tests for some nodes that were already warning properly. closes #49934
2 parents 774a6a3 + f16d2ff commit 78bcd9b

File tree

7 files changed

+184
-16
lines changed

7 files changed

+184
-16
lines changed

src/librustc/hir/intravisit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local) {
404404
// Intentionally visiting the expr first - the initialization expr
405405
// dominates the local's definition.
406406
walk_list!(visitor, visit_expr, &local.init);
407-
407+
walk_list!(visitor, visit_attribute, local.attrs.iter());
408408
visitor.visit_id(local.id);
409409
visitor.visit_pat(&local.pat);
410410
walk_list!(visitor, visit_ty, &local.ty);
@@ -731,6 +731,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Generi
731731
visitor.visit_name(ty_param.span, ty_param.name);
732732
walk_list!(visitor, visit_ty_param_bound, &ty_param.bounds);
733733
walk_list!(visitor, visit_ty, &ty_param.default);
734+
walk_list!(visitor, visit_attribute, ty_param.attrs.iter());
734735
}
735736
}
736737
}

src/librustc_resolve/macros.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ impl<'a> base::Resolver for Resolver<'a> {
206206
}
207207

208208
// Resolves attribute and derive legacy macros from `#![plugin(..)]`.
209-
fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>)
209+
fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>, allow_derive: bool)
210210
-> Option<ast::Attribute> {
211211
for i in 0..attrs.len() {
212212
let name = unwrap_or!(attrs[i].name(), continue);
@@ -227,6 +227,8 @@ impl<'a> base::Resolver for Resolver<'a> {
227227
}
228228
}
229229

230+
if !allow_derive { return None }
231+
230232
// Check for legacy derives
231233
for i in 0..attrs.len() {
232234
let name = unwrap_or!(attrs[i].name(), continue);

src/libsyntax/ast.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ impl Stmt {
821821

822822
pub fn is_item(&self) -> bool {
823823
match self.node {
824-
StmtKind::Local(_) => true,
824+
StmtKind::Item(_) => true,
825825
_ => false,
826826
}
827827
}

src/libsyntax/ext/base.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,20 @@ impl Annotatable {
118118
}
119119
}
120120

121+
pub fn expect_stmt(self) -> ast::Stmt {
122+
match self {
123+
Annotatable::Stmt(stmt) => stmt.into_inner(),
124+
_ => panic!("expected statement"),
125+
}
126+
}
127+
128+
pub fn expect_expr(self) -> P<ast::Expr> {
129+
match self {
130+
Annotatable::Expr(expr) => expr,
131+
_ => panic!("expected expression"),
132+
}
133+
}
134+
121135
pub fn derive_allowed(&self) -> bool {
122136
match *self {
123137
Annotatable::Item(ref item) => match item.node {
@@ -661,7 +675,9 @@ pub trait Resolver {
661675

662676
fn resolve_imports(&mut self);
663677
// Resolves attribute and derive legacy macros from `#![plugin(..)]`.
664-
fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>;
678+
fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<Attribute>, allow_derive: bool)
679+
-> Option<Attribute>;
680+
665681
fn resolve_invoc(&mut self, invoc: &mut Invocation, scope: Mark, force: bool)
666682
-> Result<Option<Lrc<SyntaxExtension>>, Determinacy>;
667683
fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind, force: bool)
@@ -687,7 +703,8 @@ impl Resolver for DummyResolver {
687703
fn add_builtin(&mut self, _ident: ast::Ident, _ext: Lrc<SyntaxExtension>) {}
688704

689705
fn resolve_imports(&mut self) {}
690-
fn find_legacy_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>) -> Option<Attribute> { None }
706+
fn find_legacy_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>, _allow_derive: bool)
707+
-> Option<Attribute> { None }
691708
fn resolve_invoc(&mut self, _invoc: &mut Invocation, _scope: Mark, _force: bool)
692709
-> Result<Option<Lrc<SyntaxExtension>>, Determinacy> {
693710
Err(Determinacy::Determined)

src/libsyntax/ext/expand.rs

+57-11
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ impl ExpansionKind {
143143
}
144144

145145
fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>(self, items: I) -> Expansion {
146-
let items = items.into_iter();
146+
let mut items = items.into_iter();
147147
match self {
148148
ExpansionKind::Items =>
149149
Expansion::Items(items.map(Annotatable::expect_item).collect()),
@@ -153,7 +153,14 @@ impl ExpansionKind {
153153
Expansion::TraitItems(items.map(Annotatable::expect_trait_item).collect()),
154154
ExpansionKind::ForeignItems =>
155155
Expansion::ForeignItems(items.map(Annotatable::expect_foreign_item).collect()),
156-
_ => unreachable!(),
156+
ExpansionKind::Stmts => Expansion::Stmts(items.map(Annotatable::expect_stmt).collect()),
157+
ExpansionKind::Expr => Expansion::Expr(
158+
items.next().expect("expected exactly one expression").expect_expr()
159+
),
160+
ExpansionKind::OptExpr =>
161+
Expansion::OptExpr(items.next().map(Annotatable::expect_expr)),
162+
ExpansionKind::Pat | ExpansionKind::Ty =>
163+
panic!("patterns and types aren't annotatable"),
157164
}
158165
}
159166
}
@@ -956,14 +963,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
956963
self.collect(kind, InvocationKind::Attr { attr, traits, item })
957964
}
958965

959-
// If `item` is an attr invocation, remove and return the macro attribute.
966+
/// If `item` is an attr invocation, remove and return the macro attribute and derive traits.
960967
fn classify_item<T>(&mut self, mut item: T) -> (Option<ast::Attribute>, Vec<Path>, T)
961968
where T: HasAttrs,
962969
{
963970
let (mut attr, mut traits) = (None, Vec::new());
964971

965972
item = item.map_attrs(|mut attrs| {
966-
if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs) {
973+
if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs,
974+
true) {
967975
attr = Some(legacy_attr_invoc);
968976
return attrs;
969977
}
@@ -978,6 +986,28 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
978986
(attr, traits, item)
979987
}
980988

989+
/// Alternative of `classify_item()` that ignores `#[derive]` so invocations fallthrough
990+
/// to the unused-attributes lint (making it an error on statements and expressions
991+
/// is a breaking change)
992+
fn classify_nonitem<T: HasAttrs>(&mut self, mut item: T) -> (Option<ast::Attribute>, T) {
993+
let mut attr = None;
994+
995+
item = item.map_attrs(|mut attrs| {
996+
if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs,
997+
false) {
998+
attr = Some(legacy_attr_invoc);
999+
return attrs;
1000+
}
1001+
1002+
if self.cx.ecfg.proc_macro_enabled() {
1003+
attr = find_attr_invoc(&mut attrs);
1004+
}
1005+
attrs
1006+
});
1007+
1008+
(attr, item)
1009+
}
1010+
9811011
fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
9821012
self.cfg.configure(node)
9831013
}
@@ -988,6 +1018,13 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
9881018
let features = self.cx.ecfg.features.unwrap();
9891019
for attr in attrs.iter() {
9901020
feature_gate::check_attribute(attr, self.cx.parse_sess, features);
1021+
1022+
// macros are expanded before any lint passes so this warning has to be hardcoded
1023+
if attr.path == "derive" {
1024+
self.cx.struct_span_warn(attr.span, "`#[derive]` does nothing on macro invocations")
1025+
.note("this may become a hard error in a future release")
1026+
.emit();
1027+
}
9911028
}
9921029
}
9931030

@@ -1008,15 +1045,16 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
10081045
let mut expr = self.cfg.configure_expr(expr).into_inner();
10091046
expr.node = self.cfg.configure_expr_kind(expr.node);
10101047

1011-
let (attr, derives, expr) = self.classify_item(expr);
1048+
// ignore derives so they remain unused
1049+
let (attr, expr) = self.classify_nonitem(expr);
10121050

1013-
if attr.is_some() || !derives.is_empty() {
1051+
if attr.is_some() {
10141052
// collect the invoc regardless of whether or not attributes are permitted here
10151053
// expansion will eat the attribute so it won't error later
10161054
attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a));
10171055

10181056
// ExpansionKind::Expr requires the macro to emit an expression
1019-
return self.collect_attr(attr, derives, Annotatable::Expr(P(expr)), ExpansionKind::Expr)
1057+
return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), ExpansionKind::Expr)
10201058
.make_expr();
10211059
}
10221060

@@ -1032,12 +1070,13 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
10321070
let mut expr = configure!(self, expr).into_inner();
10331071
expr.node = self.cfg.configure_expr_kind(expr.node);
10341072

1035-
let (attr, derives, expr) = self.classify_item(expr);
1073+
// ignore derives so they remain unused
1074+
let (attr, expr) = self.classify_nonitem(expr);
10361075

1037-
if attr.is_some() || !derives.is_empty() {
1076+
if attr.is_some() {
10381077
attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a));
10391078

1040-
return self.collect_attr(attr, derives, Annotatable::Expr(P(expr)),
1079+
return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)),
10411080
ExpansionKind::OptExpr)
10421081
.make_opt_expr();
10431082
}
@@ -1071,7 +1110,14 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
10711110

10721111
// we'll expand attributes on expressions separately
10731112
if !stmt.is_expr() {
1074-
let (attr, derives, stmt_) = self.classify_item(stmt);
1113+
let (attr, derives, stmt_) = if stmt.is_item() {
1114+
self.classify_item(stmt)
1115+
} else {
1116+
// ignore derives on non-item statements so it falls through
1117+
// to the unused-attributes lint
1118+
let (attr, stmt) = self.classify_nonitem(stmt);
1119+
(attr, vec![], stmt)
1120+
};
10751121

10761122
if attr.is_some() || !derives.is_empty() {
10771123
return self.collect_attr(attr, derives,

src/test/ui/issue-49934.rs

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-pass
12+
13+
#![feature(stmt_expr_attributes)]
14+
#![warn(unused_attributes)] //~ NOTE lint level defined here
15+
16+
fn foo<#[derive(Debug)] T>() { //~ WARN unused attribute
17+
match 0 {
18+
#[derive(Debug)] //~ WARN unused attribute
19+
_ => (),
20+
}
21+
}
22+
23+
fn main() {
24+
// fold_stmt (Item)
25+
#[allow(dead_code)]
26+
#[derive(Debug)] // should not warn
27+
struct Foo;
28+
29+
// fold_stmt (Mac)
30+
#[derive(Debug)]
31+
//~^ WARN `#[derive]` does nothing on macro invocations
32+
//~| NOTE this may become a hard error in a future release
33+
println!("Hello, world!");
34+
35+
// fold_stmt (Semi)
36+
#[derive(Debug)] //~ WARN unused attribute
37+
"Hello, world!";
38+
39+
// fold_stmt (Local)
40+
#[derive(Debug)] //~ WARN unused attribute
41+
let _ = "Hello, world!";
42+
43+
// fold_expr
44+
let _ = #[derive(Debug)] "Hello, world!";
45+
//~^ WARN unused attribute
46+
47+
let _ = [
48+
// fold_opt_expr
49+
#[derive(Debug)] //~ WARN unused attribute
50+
"Hello, world!"
51+
];
52+
}

src/test/ui/issue-49934.stderr

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
warning: `#[derive]` does nothing on macro invocations
2+
--> $DIR/issue-49934.rs:30:5
3+
|
4+
LL | #[derive(Debug)]
5+
| ^^^^^^^^^^^^^^^^
6+
|
7+
= note: this may become a hard error in a future release
8+
9+
warning: unused attribute
10+
--> $DIR/issue-49934.rs:16:8
11+
|
12+
LL | fn foo<#[derive(Debug)] T>() { //~ WARN unused attribute
13+
| ^^^^^^^^^^^^^^^^
14+
|
15+
note: lint level defined here
16+
--> $DIR/issue-49934.rs:14:9
17+
|
18+
LL | #![warn(unused_attributes)] //~ NOTE lint level defined here
19+
| ^^^^^^^^^^^^^^^^^
20+
21+
warning: unused attribute
22+
--> $DIR/issue-49934.rs:18:9
23+
|
24+
LL | #[derive(Debug)] //~ WARN unused attribute
25+
| ^^^^^^^^^^^^^^^^
26+
27+
warning: unused attribute
28+
--> $DIR/issue-49934.rs:36:5
29+
|
30+
LL | #[derive(Debug)] //~ WARN unused attribute
31+
| ^^^^^^^^^^^^^^^^
32+
33+
warning: unused attribute
34+
--> $DIR/issue-49934.rs:40:5
35+
|
36+
LL | #[derive(Debug)] //~ WARN unused attribute
37+
| ^^^^^^^^^^^^^^^^
38+
39+
warning: unused attribute
40+
--> $DIR/issue-49934.rs:44:13
41+
|
42+
LL | let _ = #[derive(Debug)] "Hello, world!";
43+
| ^^^^^^^^^^^^^^^^
44+
45+
warning: unused attribute
46+
--> $DIR/issue-49934.rs:49:9
47+
|
48+
LL | #[derive(Debug)] //~ WARN unused attribute
49+
| ^^^^^^^^^^^^^^^^
50+

0 commit comments

Comments
 (0)