Skip to content

Commit 0357257

Browse files
committed
Allow path as value in name-value attribute
1 parent 4c0c5e0 commit 0357257

13 files changed

+114
-14
lines changed

compiler/rustc_parse/src/parser/attr.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ impl<'a> Parser<'a> {
159159
/// PATH `{` TOKEN_STREAM `}`
160160
/// PATH
161161
/// PATH `=` UNSUFFIXED_LIT
162+
/// PATH `=` PATH
162163
/// The delimiters or `=` are still put into the resulting token stream.
163164
pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> {
164165
let item = match self.token.kind {
@@ -230,6 +231,11 @@ impl<'a> Parser<'a> {
230231

231232
crate fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
232233
let lit = self.parse_lit()?;
234+
self.require_unsuffixed(&lit);
235+
Ok(lit)
236+
}
237+
238+
crate fn require_unsuffixed(&self, lit: &ast::Lit) {
233239
debug!("checking if {:?} is unusuffixed", lit);
234240

235241
if !lit.kind.is_unsuffixed() {
@@ -240,8 +246,6 @@ impl<'a> Parser<'a> {
240246
)
241247
.emit();
242248
}
243-
244-
Ok(lit)
245249
}
246250

247251
/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.

compiler/rustc_parse/src/parser/mod.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -934,16 +934,22 @@ impl<'a> Parser<'a> {
934934
is_interpolated_expr = true;
935935
}
936936
}
937-
let token_tree = if is_interpolated_expr {
937+
let token_stream: TokenStream = if is_interpolated_expr {
938938
// We need to accept arbitrary interpolated expressions to continue
939939
// supporting things like `doc = $expr` that work on stable.
940940
// Non-literal interpolated expressions are rejected after expansion.
941-
self.parse_token_tree()
941+
self.parse_token_tree().into()
942+
} else if let Some(lit) = self.parse_opt_lit() {
943+
self.require_unsuffixed(&lit);
944+
lit.token_tree().into()
945+
} else if self.check(&token::ModSep) || self.token.ident().is_some() {
946+
self.collect_tokens_only(|this| this.parse_path(PathStyle::Mod))?
942947
} else {
943-
self.parse_unsuffixed_lit()?.token_tree()
948+
let msg = "expected a literal or ::-separated path";
949+
return Err(self.struct_span_err(self.token.span, msg));
944950
};
945951

946-
MacArgs::Eq(eq_span, token_tree.into())
952+
MacArgs::Eq(eq_span, token_stream)
947953
} else {
948954
MacArgs::Empty
949955
}
@@ -1254,6 +1260,14 @@ impl<'a> Parser<'a> {
12541260
Ok((ret, Some(LazyTokenStream::new(lazy_impl))))
12551261
}
12561262

1263+
fn collect_tokens_only<R>(
1264+
&mut self,
1265+
f: impl FnOnce(&mut Self) -> PResult<'a, R>,
1266+
) -> PResult<'a, TokenStream> {
1267+
let (_ignored, tokens) = self.collect_tokens(f)?;
1268+
Ok(tokens)
1269+
}
1270+
12571271
/// `::{` or `::*`
12581272
fn is_import_coupler(&mut self) -> bool {
12591273
self.check(&token::ModSep)

src/test/ui/attr-eq-token-tree.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
#[my_attr = !] //~ ERROR unexpected token: `!`
1+
#[my_attr = !] //~ ERROR expected a literal or ::-separated path
22
fn main() {}

src/test/ui/attr-eq-token-tree.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: unexpected token: `!`
1+
error: expected a literal or ::-separated path
22
--> $DIR/attr-eq-token-tree.rs:1:13
33
|
44
LL | #[my_attr = !]
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#[cfg(any())]
2+
extern "C++" {
3+
#[namespace = std::experimental]
4+
type any;
5+
6+
#[rust = std::option::Option<T>]
7+
//~^ ERROR expected one of `::` or `]`, found `<`
8+
type optional;
9+
}
10+
11+
fn main() {}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: expected one of `::` or `]`, found `<`
2+
--> $DIR/path-eq-path.rs:6:33
3+
|
4+
LL | extern "C++" {
5+
| - while parsing this item list starting here
6+
...
7+
LL | #[rust = std::option::Option<T>]
8+
| ^ expected one of `::` or `]`
9+
...
10+
LL | }
11+
| - the item list ends here
12+
13+
error: aborting due to previous error
14+

src/test/ui/macros/macro-attribute.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
#[doc = $not_there] //~ ERROR unexpected token: `$`
1+
#[doc = $not_there] //~ ERROR expected a literal or ::-separated path
22
fn main() { }

src/test/ui/macros/macro-attribute.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: unexpected token: `$`
1+
error: expected a literal or ::-separated path
22
--> $DIR/macro-attribute.rs:1:9
33
|
44
LL | #[doc = $not_there]

src/test/ui/parser/attr-bad-meta-2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
#[path =] //~ ERROR unexpected token: `]`
1+
#[path =] //~ ERROR expected a literal or ::-separated path
22
mod m {}

src/test/ui/parser/attr-bad-meta-2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: unexpected token: `]`
1+
error: expected a literal or ::-separated path
22
--> $DIR/attr-bad-meta-2.rs:1:9
33
|
44
LL | #[path =]

src/test/ui/proc-macro/attributes-included.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use attributes_included::*;
1313
#[foo]
1414
#[inline]
1515
/// doc
16+
#[namespace = std::experimental]
1617
fn foo() {
1718
let a: i32 = "foo"; //~ WARN: unused variable
1819
}

src/test/ui/proc-macro/attributes-included.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
warning: unused variable: `a`
2-
--> $DIR/attributes-included.rs:17:9
2+
--> $DIR/attributes-included.rs:18:9
33
|
44
LL | let a: i32 = "foo";
55
| ^ help: if this is intentional, prefix it with an underscore: `_a`

src/test/ui/proc-macro/auxiliary/attributes-included.rs

+57-1
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,30 @@
66
extern crate proc_macro;
77

88
use proc_macro::{TokenStream, TokenTree, Delimiter, Literal, Spacing, Group};
9+
use std::iter;
910

1011
#[proc_macro_attribute]
1112
pub fn foo(attr: TokenStream, input: TokenStream) -> TokenStream {
1213
assert!(attr.is_empty());
13-
let input = input.into_iter().collect::<Vec<_>>();
14+
let mut input = input.into_iter().collect::<Vec<_>>();
15+
let (namespace_start, namespace_end);
1416
{
1517
let mut cursor = &input[..];
1618
assert_inline(&mut cursor);
1719
assert_doc(&mut cursor);
1820
assert_inline(&mut cursor);
1921
assert_doc(&mut cursor);
22+
23+
// Splice out the #[namespace = ...] attribute as it is an inert
24+
// attribute, not a proc macro.
25+
namespace_start = input.len() - cursor.len();
26+
assert_namespace(&mut cursor);
27+
namespace_end = input.len() - cursor.len();
28+
2029
assert_foo(&mut cursor);
2130
assert!(cursor.is_empty());
2231
}
32+
input.splice(namespace_start..namespace_end, iter::empty());
2333
fold_stream(input.into_iter().collect())
2434
}
2535

@@ -34,6 +44,7 @@ pub fn bar(attr: TokenStream, input: TokenStream) -> TokenStream {
3444
assert_invoc(&mut cursor);
3545
assert_inline(&mut cursor);
3646
assert_doc(&mut cursor);
47+
assert_namespace(&mut cursor);
3748
assert_foo(&mut cursor);
3849
assert!(cursor.is_empty());
3950
}
@@ -93,6 +104,51 @@ fn assert_doc(slice: &mut &[TokenTree]) {
93104
*slice = &slice[2..];
94105
}
95106

107+
fn assert_namespace(slice: &mut &[TokenTree]) {
108+
match &slice[0] {
109+
TokenTree::Punct(tt) => assert_eq!(tt.as_char(), '#'),
110+
_ => panic!("expected `#`"),
111+
}
112+
let inner = match &slice[1] {
113+
TokenTree::Group(tt) => tt.stream().into_iter().collect::<Vec<_>>(),
114+
_ => panic!("expected brackets"),
115+
};
116+
if inner.len() != 6 {
117+
panic!("expected 6 tokens in namespace attr")
118+
}
119+
match &inner[0] {
120+
TokenTree::Ident(tt) => assert_eq!("namespace", tt.to_string()),
121+
_ => panic!("expected `namespace`"),
122+
}
123+
match &inner[1] {
124+
TokenTree::Punct(tt) => assert_eq!(tt.as_char(), '='),
125+
_ => panic!("expected `=`"),
126+
}
127+
match &inner[2] {
128+
TokenTree::Ident(tt) => assert_eq!("std", tt.to_string()),
129+
_ => panic!("expected `std`"),
130+
}
131+
match &inner[3] {
132+
TokenTree::Punct(tt) => {
133+
assert_eq!(tt.as_char(), ':');
134+
assert_eq!(tt.spacing(), Spacing::Joint);
135+
}
136+
_ => panic!("expected `:`"),
137+
}
138+
match &inner[4] {
139+
TokenTree::Punct(tt) => {
140+
assert_eq!(tt.as_char(), ':');
141+
assert_eq!(tt.spacing(), Spacing::Alone);
142+
}
143+
_ => panic!("expected `:`"),
144+
}
145+
match &inner[5] {
146+
TokenTree::Ident(tt) => assert_eq!("experimental", tt.to_string()),
147+
_ => panic!("expected `experimental`"),
148+
}
149+
*slice = &slice[2..];
150+
}
151+
96152
fn assert_invoc(slice: &mut &[TokenTree]) {
97153
match &slice[0] {
98154
TokenTree::Punct(tt) => assert_eq!(tt.as_char(), '#'),

0 commit comments

Comments
 (0)