|
1 | 1 | use crate::base::{self, *};
|
2 | 2 | use crate::proc_macro_server;
|
3 | 3 |
|
4 |
| -use syntax::ast::{self, ItemKind, MacArgs}; |
| 4 | +use syntax::ast::{self, ItemKind, MetaItemKind, NestedMetaItem}; |
5 | 5 | use syntax::errors::{Applicability, FatalError};
|
6 | 6 | use syntax::symbol::sym;
|
7 | 7 | use syntax::token;
|
@@ -171,34 +171,71 @@ crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>)
|
171 | 171 | if !attr.has_name(sym::derive) {
|
172 | 172 | return true;
|
173 | 173 | }
|
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 |
| - } |
184 | 174 |
|
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; |
188 | 187 | }
|
189 |
| - rustc_parse::parse_in_attr(cx.parse_sess, attr, |p| p.parse_derive_paths()) |
| 188 | + Some(x) => x, |
190 | 189 | };
|
191 | 190 |
|
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 |
202 | 239 | });
|
203 | 240 | result
|
204 | 241 | }
|
0 commit comments