derive: avoid parse_in_attr
This commit is contained in:
parent
bbcda98d41
commit
cbc9f68312
7 changed files with 90 additions and 80 deletions
|
@ -285,7 +285,7 @@ pub fn parse_in<'a, T>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
|
/// 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,
|
sess: &'a ParseSess,
|
||||||
attr: &ast::Attribute,
|
attr: &ast::Attribute,
|
||||||
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
|
||||||
|
|
|
@ -3,7 +3,6 @@ use crate::maybe_whole;
|
||||||
use rustc_errors::{PResult, Applicability, pluralize};
|
use rustc_errors::{PResult, Applicability, pluralize};
|
||||||
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
|
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
|
||||||
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
|
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
|
||||||
use syntax::ast::MacArgs;
|
|
||||||
use syntax::ThinVec;
|
use syntax::ThinVec;
|
||||||
use syntax::token::{self, Token};
|
use syntax::token::{self, Token};
|
||||||
use syntax_pos::source_map::{Span, BytePos};
|
use syntax_pos::source_map::{Span, BytePos};
|
||||||
|
@ -109,42 +108,6 @@ impl<'a> Parser<'a> {
|
||||||
Ok(Path { segments, span: lo.to(self.prev_span) })
|
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(
|
pub(super) fn parse_path_segments(
|
||||||
&mut self,
|
&mut self,
|
||||||
segments: &mut Vec<PathSegment>,
|
segments: &mut Vec<PathSegment>,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::base::{self, *};
|
use crate::base::{self, *};
|
||||||
use crate::proc_macro_server;
|
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::errors::{Applicability, FatalError};
|
||||||
use syntax::symbol::sym;
|
use syntax::symbol::sym;
|
||||||
use syntax::token;
|
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) {
|
if !attr.has_name(sym::derive) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if !attr.is_meta_item_list() {
|
|
||||||
|
// 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")
|
cx.struct_span_err(attr.span, "malformed `derive` attribute input")
|
||||||
.span_suggestion(
|
.span_suggestion(
|
||||||
attr.span,
|
attr.span,
|
||||||
"missing traits to be derived",
|
"missing traits to be derived",
|
||||||
"#[derive(Trait1, Trait2, ...)]".to_owned(),
|
"#[derive(Trait1, Trait2, ...)]".to_owned(),
|
||||||
Applicability::HasPlaceholders,
|
Applicability::HasPlaceholders,
|
||||||
).emit();
|
)
|
||||||
|
.emit();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Some(x) => x,
|
||||||
let parse_derive_paths = |attr: &ast::Attribute| {
|
|
||||||
if let MacArgs::Empty = attr.get_normal_item().args {
|
|
||||||
return Ok(Vec::new());
|
|
||||||
}
|
|
||||||
rustc_parse::parse_in_attr(cx.parse_sess, attr, |p| p.parse_derive_paths())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match parse_derive_paths(attr) {
|
let mut retain_in_fm = true;
|
||||||
Ok(traits) => {
|
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);
|
result.extend(traits);
|
||||||
true
|
retain_in_fm && retain_in_map
|
||||||
}
|
|
||||||
Err(mut e) => {
|
|
||||||
e.emit();
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
result
|
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;
|
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;
|
struct Test2;
|
||||||
|
|
||||||
#[derive] //~ ERROR malformed `derive` attribute input
|
#[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
|
--> $DIR/malformed-derive-entry.rs:1:14
|
||||||
|
|
|
|
||||||
LL | #[derive(Copy(Bad))]
|
LL | #[derive(Copy(Bad))]
|
||||||
| ^ expected one of `)`, `,`, or `::`
|
| ^^^^^ help: remove the arguments
|
||||||
|
|
||||||
error: expected one of `)`, `,`, or `::`, found `=`
|
error: traits in `#[derive(...)]` don't accept values
|
||||||
--> $DIR/malformed-derive-entry.rs:4:14
|
--> $DIR/malformed-derive-entry.rs:6:14
|
||||||
|
|
|
|
||||||
LL | #[derive(Copy="bad")]
|
LL | #[derive(Copy="bad")]
|
||||||
| ^ expected one of `)`, `,`, or `::`
|
| ^^^^^^ help: remove the value
|
||||||
|
|
||||||
error: malformed `derive` attribute input
|
error: malformed `derive` attribute input
|
||||||
--> $DIR/malformed-derive-entry.rs:7:1
|
--> $DIR/malformed-derive-entry.rs:11:1
|
||||||
|
|
|
|
||||||
LL | #[derive]
|
LL | #[derive]
|
||||||
| ^^^^^^^^^ help: missing traits to be derived: `#[derive(Trait1, Trait2, ...)]`
|
| ^^^^^^^^^ 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::<T>!(); //~ ERROR generic arguments in macro path
|
||||||
foo::<>!(); //~ ERROR generic arguments in macro path
|
foo::<>!(); //~ ERROR generic arguments in macro path
|
||||||
m!(Default<>); //~ 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::<>!();
|
LL | foo::<>!();
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: unexpected generic arguments in path
|
|
||||||
--> $DIR/macro-ty-params.rs:12:8
|
|
||||||
|
|
|
||||||
LL | m!(Default<>);
|
|
||||||
| ^^^^^^^^^
|
|
||||||
|
|
||||||
error: generic arguments in macro path
|
error: generic arguments in macro path
|
||||||
--> $DIR/macro-ty-params.rs:12:15
|
--> $DIR/macro-ty-params.rs:12:15
|
||||||
|
|
|
|
||||||
LL | m!(Default<>);
|
LL | m!(Default<>);
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue