Skip to content

Commit cbc9f68

Browse files
committed
derive: avoid parse_in_attr
1 parent bbcda98 commit cbc9f68

File tree

7 files changed

+90
-80
lines changed

7 files changed

+90
-80
lines changed

src/librustc_parse/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ pub fn parse_in<'a, T>(
285285
}
286286

287287
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
288-
pub fn parse_in_attr<'a, T>(
288+
fn parse_in_attr<'a, T>(
289289
sess: &'a ParseSess,
290290
attr: &ast::Attribute,
291291
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,

src/librustc_parse/parser/path.rs

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::maybe_whole;
33
use rustc_errors::{PResult, Applicability, pluralize};
44
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
55
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
6-
use syntax::ast::MacArgs;
76
use syntax::ThinVec;
87
use syntax::token::{self, Token};
98
use syntax_pos::source_map::{Span, BytePos};
@@ -109,42 +108,6 @@ impl<'a> Parser<'a> {
109108
Ok(Path { segments, span: lo.to(self.prev_span) })
110109
}
111110

112-
/// Like `parse_path`, but also supports parsing `Word` meta items into paths for
113-
/// backwards-compatibility. This is used when parsing derive macro paths in `#[derive]`
114-
/// attributes.
115-
fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, Path> {
116-
let meta_ident = match self.token.kind {
117-
token::Interpolated(ref nt) => match **nt {
118-
token::NtMeta(ref item) => match item.args {
119-
MacArgs::Empty => Some(item.path.clone()),
120-
_ => None,
121-
},
122-
_ => None,
123-
},
124-
_ => None,
125-
};
126-
if let Some(path) = meta_ident {
127-
self.bump();
128-
return Ok(path);
129-
}
130-
self.parse_path(style)
131-
}
132-
133-
/// Parse a list of paths inside `#[derive(path_0, ..., path_n)]`.
134-
pub fn parse_derive_paths(&mut self) -> PResult<'a, Vec<Path>> {
135-
self.expect(&token::OpenDelim(token::Paren))?;
136-
let mut list = Vec::new();
137-
while !self.eat(&token::CloseDelim(token::Paren)) {
138-
let path = self.parse_path_allowing_meta(PathStyle::Mod)?;
139-
list.push(path);
140-
if !self.eat(&token::Comma) {
141-
self.expect(&token::CloseDelim(token::Paren))?;
142-
break
143-
}
144-
}
145-
Ok(list)
146-
}
147-
148111
pub(super) fn parse_path_segments(
149112
&mut self,
150113
segments: &mut Vec<PathSegment>,

src/libsyntax_expand/proc_macro.rs

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::base::{self, *};
22
use crate::proc_macro_server;
33

4-
use syntax::ast::{self, ItemKind, MacArgs};
4+
use syntax::ast::{self, ItemKind, MetaItemKind, NestedMetaItem};
55
use syntax::errors::{Applicability, FatalError};
66
use syntax::symbol::sym;
77
use syntax::token;
@@ -171,34 +171,71 @@ crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>)
171171
if !attr.has_name(sym::derive) {
172172
return true;
173173
}
174-
if !attr.is_meta_item_list() {
175-
cx.struct_span_err(attr.span, "malformed `derive` attribute input")
176-
.span_suggestion(
177-
attr.span,
178-
"missing traits to be derived",
179-
"#[derive(Trait1, Trait2, ...)]".to_owned(),
180-
Applicability::HasPlaceholders,
181-
).emit();
182-
return false;
183-
}
184174

185-
let parse_derive_paths = |attr: &ast::Attribute| {
186-
if let MacArgs::Empty = attr.get_normal_item().args {
187-
return Ok(Vec::new());
175+
// 1) First let's ensure that it's a meta item.
176+
let nmis = match attr.meta_item_list() {
177+
None => {
178+
cx.struct_span_err(attr.span, "malformed `derive` attribute input")
179+
.span_suggestion(
180+
attr.span,
181+
"missing traits to be derived",
182+
"#[derive(Trait1, Trait2, ...)]".to_owned(),
183+
Applicability::HasPlaceholders,
184+
)
185+
.emit();
186+
return false;
188187
}
189-
rustc_parse::parse_in_attr(cx.parse_sess, attr, |p| p.parse_derive_paths())
188+
Some(x) => x,
190189
};
191190

192-
match parse_derive_paths(attr) {
193-
Ok(traits) => {
194-
result.extend(traits);
195-
true
196-
}
197-
Err(mut e) => {
198-
e.emit();
199-
false
200-
}
201-
}
191+
let mut retain_in_fm = true;
192+
let mut retain_in_map = true;
193+
let traits = nmis
194+
.into_iter()
195+
// 2) Moreover, let's ensure we have a path and not `#[derive("foo")]`.
196+
.filter_map(|nmi| match nmi {
197+
NestedMetaItem::Literal(lit) => {
198+
retain_in_fm = false;
199+
cx.struct_span_err(lit.span, "expected path to a trait, found literal")
200+
.help("for example, write `#[derive(Debug)]` for `Debug`")
201+
.emit();
202+
None
203+
}
204+
NestedMetaItem::MetaItem(mi) => Some(mi),
205+
})
206+
// 3) Finally, we only accept `#[derive($path_0, $path_1, ..)]`
207+
// but not e.g. `#[derive($path_0 = "value", $path_1(abc))]`.
208+
// In this case we can still at least determine that the user
209+
// wanted this trait to be derived, so let's keep it.
210+
.map(|mi| {
211+
let mut traits_dont_accept = |title, action| {
212+
retain_in_map = false;
213+
let sp = mi.span.with_lo(mi.path.span.hi());
214+
cx.struct_span_err(sp, title)
215+
.span_suggestion(
216+
sp,
217+
action,
218+
String::new(),
219+
Applicability::MachineApplicable,
220+
)
221+
.emit();
222+
};
223+
match &mi.kind {
224+
MetaItemKind::List(..) => traits_dont_accept(
225+
"traits in `#[derive(...)]` don't accept arguments",
226+
"remove the arguments",
227+
),
228+
MetaItemKind::NameValue(..) => traits_dont_accept(
229+
"traits in `#[derive(...)]` don't accept values",
230+
"remove the value",
231+
),
232+
MetaItemKind::Word => {}
233+
}
234+
mi.path
235+
});
236+
237+
result.extend(traits);
238+
retain_in_fm && retain_in_map
202239
});
203240
result
204241
}

src/test/ui/malformed/malformed-derive-entry.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
#[derive(Copy(Bad))] //~ ERROR expected one of `)`, `,`, or `::`, found `(`
1+
#[derive(Copy(Bad))]
2+
//~^ ERROR traits in `#[derive(...)]` don't accept arguments
3+
//~| ERROR the trait bound
24
struct Test1;
35

4-
#[derive(Copy="bad")] //~ ERROR expected one of `)`, `,`, or `::`, found `=`
6+
#[derive(Copy="bad")]
7+
//~^ ERROR traits in `#[derive(...)]` don't accept values
8+
//~| ERROR the trait bound
59
struct Test2;
610

711
#[derive] //~ ERROR malformed `derive` attribute input
Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,33 @@
1-
error: expected one of `)`, `,`, or `::`, found `(`
1+
error: traits in `#[derive(...)]` don't accept arguments
22
--> $DIR/malformed-derive-entry.rs:1:14
33
|
44
LL | #[derive(Copy(Bad))]
5-
| ^ expected one of `)`, `,`, or `::`
5+
| ^^^^^ help: remove the arguments
66

7-
error: expected one of `)`, `,`, or `::`, found `=`
8-
--> $DIR/malformed-derive-entry.rs:4:14
7+
error: traits in `#[derive(...)]` don't accept values
8+
--> $DIR/malformed-derive-entry.rs:6:14
99
|
1010
LL | #[derive(Copy="bad")]
11-
| ^ expected one of `)`, `,`, or `::`
11+
| ^^^^^^ help: remove the value
1212

1313
error: malformed `derive` attribute input
14-
--> $DIR/malformed-derive-entry.rs:7:1
14+
--> $DIR/malformed-derive-entry.rs:11:1
1515
|
1616
LL | #[derive]
1717
| ^^^^^^^^^ help: missing traits to be derived: `#[derive(Trait1, Trait2, ...)]`
1818

19-
error: aborting due to 3 previous errors
19+
error[E0277]: the trait bound `Test1: std::clone::Clone` is not satisfied
20+
--> $DIR/malformed-derive-entry.rs:1:10
21+
|
22+
LL | #[derive(Copy(Bad))]
23+
| ^^^^ the trait `std::clone::Clone` is not implemented for `Test1`
24+
25+
error[E0277]: the trait bound `Test2: std::clone::Clone` is not satisfied
26+
--> $DIR/malformed-derive-entry.rs:6:10
27+
|
28+
LL | #[derive(Copy="bad")]
29+
| ^^^^ the trait `std::clone::Clone` is not implemented for `Test2`
30+
31+
error: aborting due to 5 previous errors
2032

33+
For more information about this error, try `rustc --explain E0277`.

src/test/ui/span/macro-ty-params.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,4 @@ fn main() {
1010
foo::<T>!(); //~ ERROR generic arguments in macro path
1111
foo::<>!(); //~ ERROR generic arguments in macro path
1212
m!(Default<>); //~ ERROR generic arguments in macro path
13-
//~^ ERROR unexpected generic arguments in path
1413
}

src/test/ui/span/macro-ty-params.stderr

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,11 @@ error: generic arguments in macro path
1010
LL | foo::<>!();
1111
| ^^
1212

13-
error: unexpected generic arguments in path
14-
--> $DIR/macro-ty-params.rs:12:8
15-
|
16-
LL | m!(Default<>);
17-
| ^^^^^^^^^
18-
1913
error: generic arguments in macro path
2014
--> $DIR/macro-ty-params.rs:12:15
2115
|
2216
LL | m!(Default<>);
2317
| ^^
2418

25-
error: aborting due to 4 previous errors
19+
error: aborting due to 3 previous errors
2620

0 commit comments

Comments
 (0)