mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 15:01:51 +00:00
derive: avoid parse_in_attr
This commit is contained in:
parent
bbcda98d41
commit
cbc9f68312
@ -285,7 +285,7 @@ pub fn parse_in<'a, T>(
|
||||
}
|
||||
|
||||
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
|
||||
pub fn parse_in_attr<'a, T>(
|
||||
fn parse_in_attr<'a, T>(
|
||||
sess: &'a ParseSess,
|
||||
attr: &ast::Attribute,
|
||||
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
||||
|
@ -3,7 +3,6 @@ use crate::maybe_whole;
|
||||
use rustc_errors::{PResult, Applicability, pluralize};
|
||||
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
|
||||
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
|
||||
use syntax::ast::MacArgs;
|
||||
use syntax::ThinVec;
|
||||
use syntax::token::{self, Token};
|
||||
use syntax_pos::source_map::{Span, BytePos};
|
||||
@ -109,42 +108,6 @@ impl<'a> Parser<'a> {
|
||||
Ok(Path { segments, span: lo.to(self.prev_span) })
|
||||
}
|
||||
|
||||
/// Like `parse_path`, but also supports parsing `Word` meta items into paths for
|
||||
/// backwards-compatibility. This is used when parsing derive macro paths in `#[derive]`
|
||||
/// attributes.
|
||||
fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, Path> {
|
||||
let meta_ident = match self.token.kind {
|
||||
token::Interpolated(ref nt) => match **nt {
|
||||
token::NtMeta(ref item) => match item.args {
|
||||
MacArgs::Empty => Some(item.path.clone()),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
if let Some(path) = meta_ident {
|
||||
self.bump();
|
||||
return Ok(path);
|
||||
}
|
||||
self.parse_path(style)
|
||||
}
|
||||
|
||||
/// Parse a list of paths inside `#[derive(path_0, ..., path_n)]`.
|
||||
pub fn parse_derive_paths(&mut self) -> PResult<'a, Vec<Path>> {
|
||||
self.expect(&token::OpenDelim(token::Paren))?;
|
||||
let mut list = Vec::new();
|
||||
while !self.eat(&token::CloseDelim(token::Paren)) {
|
||||
let path = self.parse_path_allowing_meta(PathStyle::Mod)?;
|
||||
list.push(path);
|
||||
if !self.eat(&token::Comma) {
|
||||
self.expect(&token::CloseDelim(token::Paren))?;
|
||||
break
|
||||
}
|
||||
}
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
pub(super) fn parse_path_segments(
|
||||
&mut self,
|
||||
segments: &mut Vec<PathSegment>,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::base::{self, *};
|
||||
use crate::proc_macro_server;
|
||||
|
||||
use syntax::ast::{self, ItemKind, MacArgs};
|
||||
use syntax::ast::{self, ItemKind, MetaItemKind, NestedMetaItem};
|
||||
use syntax::errors::{Applicability, FatalError};
|
||||
use syntax::symbol::sym;
|
||||
use syntax::token;
|
||||
@ -171,34 +171,71 @@ crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>)
|
||||
if !attr.has_name(sym::derive) {
|
||||
return true;
|
||||
}
|
||||
if !attr.is_meta_item_list() {
|
||||
cx.struct_span_err(attr.span, "malformed `derive` attribute input")
|
||||
.span_suggestion(
|
||||
attr.span,
|
||||
"missing traits to be derived",
|
||||
"#[derive(Trait1, Trait2, ...)]".to_owned(),
|
||||
Applicability::HasPlaceholders,
|
||||
).emit();
|
||||
return false;
|
||||
}
|
||||
|
||||
let parse_derive_paths = |attr: &ast::Attribute| {
|
||||
if let MacArgs::Empty = attr.get_normal_item().args {
|
||||
return Ok(Vec::new());
|
||||
// 1) First let's ensure that it's a meta item.
|
||||
let nmis = match attr.meta_item_list() {
|
||||
None => {
|
||||
cx.struct_span_err(attr.span, "malformed `derive` attribute input")
|
||||
.span_suggestion(
|
||||
attr.span,
|
||||
"missing traits to be derived",
|
||||
"#[derive(Trait1, Trait2, ...)]".to_owned(),
|
||||
Applicability::HasPlaceholders,
|
||||
)
|
||||
.emit();
|
||||
return false;
|
||||
}
|
||||
rustc_parse::parse_in_attr(cx.parse_sess, attr, |p| p.parse_derive_paths())
|
||||
Some(x) => x,
|
||||
};
|
||||
|
||||
match parse_derive_paths(attr) {
|
||||
Ok(traits) => {
|
||||
result.extend(traits);
|
||||
true
|
||||
}
|
||||
Err(mut e) => {
|
||||
e.emit();
|
||||
false
|
||||
}
|
||||
}
|
||||
let mut retain_in_fm = true;
|
||||
let mut retain_in_map = true;
|
||||
let traits = nmis
|
||||
.into_iter()
|
||||
// 2) Moreover, let's ensure we have a path and not `#[derive("foo")]`.
|
||||
.filter_map(|nmi| match nmi {
|
||||
NestedMetaItem::Literal(lit) => {
|
||||
retain_in_fm = false;
|
||||
cx.struct_span_err(lit.span, "expected path to a trait, found literal")
|
||||
.help("for example, write `#[derive(Debug)]` for `Debug`")
|
||||
.emit();
|
||||
None
|
||||
}
|
||||
NestedMetaItem::MetaItem(mi) => Some(mi),
|
||||
})
|
||||
// 3) Finally, we only accept `#[derive($path_0, $path_1, ..)]`
|
||||
// but not e.g. `#[derive($path_0 = "value", $path_1(abc))]`.
|
||||
// In this case we can still at least determine that the user
|
||||
// wanted this trait to be derived, so let's keep it.
|
||||
.map(|mi| {
|
||||
let mut traits_dont_accept = |title, action| {
|
||||
retain_in_map = false;
|
||||
let sp = mi.span.with_lo(mi.path.span.hi());
|
||||
cx.struct_span_err(sp, title)
|
||||
.span_suggestion(
|
||||
sp,
|
||||
action,
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
};
|
||||
match &mi.kind {
|
||||
MetaItemKind::List(..) => traits_dont_accept(
|
||||
"traits in `#[derive(...)]` don't accept arguments",
|
||||
"remove the arguments",
|
||||
),
|
||||
MetaItemKind::NameValue(..) => traits_dont_accept(
|
||||
"traits in `#[derive(...)]` don't accept values",
|
||||
"remove the value",
|
||||
),
|
||||
MetaItemKind::Word => {}
|
||||
}
|
||||
mi.path
|
||||
});
|
||||
|
||||
result.extend(traits);
|
||||
retain_in_fm && retain_in_map
|
||||
});
|
||||
result
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
#[derive(Copy(Bad))] //~ ERROR expected one of `)`, `,`, or `::`, found `(`
|
||||
#[derive(Copy(Bad))]
|
||||
//~^ ERROR traits in `#[derive(...)]` don't accept arguments
|
||||
//~| ERROR the trait bound
|
||||
struct Test1;
|
||||
|
||||
#[derive(Copy="bad")] //~ ERROR expected one of `)`, `,`, or `::`, found `=`
|
||||
#[derive(Copy="bad")]
|
||||
//~^ ERROR traits in `#[derive(...)]` don't accept values
|
||||
//~| ERROR the trait bound
|
||||
struct Test2;
|
||||
|
||||
#[derive] //~ ERROR malformed `derive` attribute input
|
||||
|
@ -1,20 +1,33 @@
|
||||
error: expected one of `)`, `,`, or `::`, found `(`
|
||||
error: traits in `#[derive(...)]` don't accept arguments
|
||||
--> $DIR/malformed-derive-entry.rs:1:14
|
||||
|
|
||||
LL | #[derive(Copy(Bad))]
|
||||
| ^ expected one of `)`, `,`, or `::`
|
||||
| ^^^^^ help: remove the arguments
|
||||
|
||||
error: expected one of `)`, `,`, or `::`, found `=`
|
||||
--> $DIR/malformed-derive-entry.rs:4:14
|
||||
error: traits in `#[derive(...)]` don't accept values
|
||||
--> $DIR/malformed-derive-entry.rs:6:14
|
||||
|
|
||||
LL | #[derive(Copy="bad")]
|
||||
| ^ expected one of `)`, `,`, or `::`
|
||||
| ^^^^^^ help: remove the value
|
||||
|
||||
error: malformed `derive` attribute input
|
||||
--> $DIR/malformed-derive-entry.rs:7:1
|
||||
--> $DIR/malformed-derive-entry.rs:11:1
|
||||
|
|
||||
LL | #[derive]
|
||||
| ^^^^^^^^^ help: missing traits to be derived: `#[derive(Trait1, Trait2, ...)]`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error[E0277]: the trait bound `Test1: std::clone::Clone` is not satisfied
|
||||
--> $DIR/malformed-derive-entry.rs:1:10
|
||||
|
|
||||
LL | #[derive(Copy(Bad))]
|
||||
| ^^^^ the trait `std::clone::Clone` is not implemented for `Test1`
|
||||
|
||||
error[E0277]: the trait bound `Test2: std::clone::Clone` is not satisfied
|
||||
--> $DIR/malformed-derive-entry.rs:6:10
|
||||
|
|
||||
LL | #[derive(Copy="bad")]
|
||||
| ^^^^ the trait `std::clone::Clone` is not implemented for `Test2`
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
@ -10,5 +10,4 @@ fn main() {
|
||||
foo::<T>!(); //~ ERROR generic arguments in macro path
|
||||
foo::<>!(); //~ ERROR generic arguments in macro path
|
||||
m!(Default<>); //~ ERROR generic arguments in macro path
|
||||
//~^ ERROR unexpected generic arguments in path
|
||||
}
|
||||
|
@ -10,17 +10,11 @@ error: generic arguments in macro path
|
||||
LL | foo::<>!();
|
||||
| ^^
|
||||
|
||||
error: unexpected generic arguments in path
|
||||
--> $DIR/macro-ty-params.rs:12:8
|
||||
|
|
||||
LL | m!(Default<>);
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: generic arguments in macro path
|
||||
--> $DIR/macro-ty-params.rs:12:15
|
||||
|
|
||||
LL | m!(Default<>);
|
||||
| ^^
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user