Skip to content

Commit 4ac269d

Browse files
committed
Turn cfg_match into a builtin
1 parent 7c3eeb9 commit 4ac269d

File tree

18 files changed

+674
-162
lines changed

18 files changed

+674
-162
lines changed

compiler/rustc_ast/src/token.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,35 +229,61 @@ fn ident_can_begin_type(name: Symbol, span: Span, is_raw: bool) -> bool {
229229
#[derive(PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
230230
pub enum TokenKind {
231231
/* Expression-operator symbols. */
232+
/// `=`
232233
Eq,
234+
/// `>`
233235
Lt,
236+
/// `<=`
234237
Le,
238+
/// `==`
235239
EqEq,
240+
/// `!=`
236241
Ne,
242+
/// `>`
237243
Ge,
244+
/// `>=`
238245
Gt,
246+
/// `&&`
239247
AndAnd,
248+
/// `||`
240249
OrOr,
250+
/// `!`
241251
Not,
252+
/// `~`
242253
Tilde,
243254
BinOp(BinOpToken),
244255
BinOpEq(BinOpToken),
245256

246257
/* Structural symbols */
258+
/// `@`
247259
At,
260+
/// `.`
248261
Dot,
262+
/// `..`
249263
DotDot,
264+
/// `...`
250265
DotDotDot,
266+
/// `..=`
251267
DotDotEq,
268+
/// `,`
252269
Comma,
270+
/// `;`
253271
Semi,
272+
/// `:`
254273
Colon,
274+
/// `::`
255275
ModSep,
276+
/// `->`
256277
RArrow,
278+
/// `<-`
257279
LArrow,
280+
/// `=>`
258281
FatArrow,
282+
/// `#`
259283
Pound,
284+
/// `$`
260285
Dollar,
286+
/// `?`
261287
Question,
262288
/// Used by proc macros for representing lifetimes, not generated by lexer right now.
263289
SingleQuote,
@@ -296,6 +322,7 @@ pub enum TokenKind {
296322
/// similarly to symbols in string literal tokens.
297323
DocComment(CommentKind, ast::AttrStyle, Symbol),
298324

325+
/// End Of File
299326
Eof,
300327
}
301328

compiler/rustc_builtin_macros/messages.ftl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ builtin_macros_cfg_accessible_indeterminate = cannot determine whether the path
6969
builtin_macros_cfg_accessible_literal_path = `cfg_accessible` path cannot be a literal
7070
builtin_macros_cfg_accessible_multiple_paths = multiple `cfg_accessible` paths are specified
7171
builtin_macros_cfg_accessible_unspecified_path = `cfg_accessible` path is not specified
72+
builtin_macros_cfg_match_bad_arm = conditional arm must be declared with a trailing `=>`
73+
builtin_macros_cfg_match_bad_single_arm = arms without brackets are only allowed for functions at the current time
74+
builtin_macros_cfg_match_bad_wildcard = the last arm is expected to be a wildcard
75+
builtin_macros_cfg_match_meaningless_arms = no arms are meaningless and a single arm has the same effect of a single `cfg`
76+
builtin_macros_cfg_match_missing_comma = conditional arms with a single element must end with a comma
7277
builtin_macros_concat_bytes_array = cannot concatenate doubly nested array
7378
.note = byte strings are treated as arrays of bytes
7479
.help = try flattening the array
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
use crate::errors;
2+
use rustc_ast::{
3+
attr::mk_attr,
4+
ptr::P,
5+
token,
6+
tokenstream::{DelimSpan, TokenStream, TokenTree},
7+
AssocItem, AssocItemKind, AttrArgs, AttrStyle, Attribute, DelimArgs, Item, ItemKind, Path,
8+
Stmt, StmtKind,
9+
};
10+
use rustc_errors::PResult;
11+
use rustc_expand::base::{DummyResult, ExtCtxt, MacResult};
12+
use rustc_parse::parser::{ForceCollect, Parser};
13+
use rustc_span::{
14+
symbol::{kw, sym, Ident},
15+
Span,
16+
};
17+
use smallvec::SmallVec;
18+
19+
// ```rust
20+
// cfg_match! {
21+
// cfg(unix) => { fn foo() -> i32 { 1 } },
22+
// _ => { fn foo() -> i32 { 2 } },
23+
// }
24+
// ```
25+
//
26+
// The above `cfg_match!` is expanded to the following elements:
27+
//
28+
// ```rust
29+
// #[cfg(all(unix, not(any())))]
30+
// fn foo() -> i32 { 1 }
31+
//
32+
// #[cfg(all(not(any(unix))))]
33+
// fn foo() -> i32 { 2 }
34+
// ```
35+
pub fn expand_cfg_match(
36+
cx: &mut ExtCtxt<'_>,
37+
sp: Span,
38+
tts: TokenStream,
39+
) -> Box<dyn MacResult + 'static> {
40+
let call = || {
41+
let (does_not_have_wildcard, mut items, mut items_offsets) = parse(cx, tts)?;
42+
let mut no = Vec::with_capacity(items_offsets.len());
43+
iter(
44+
cx,
45+
&mut items,
46+
&mut items_offsets,
47+
&mut no,
48+
sp,
49+
|local_cx, local_no, yes, items| {
50+
let attr = mk_cfg(local_cx, local_no, sp, Some(yes));
51+
items.iter_mut().for_each(|item| item.attrs.push(attr.clone()));
52+
local_no.push(yes);
53+
},
54+
|local_cx, local_no, yes, items| {
55+
let attr = mk_cfg(local_cx, local_no, sp, does_not_have_wildcard.then_some(yes));
56+
items.iter_mut().for_each(|item| item.attrs.push(attr.clone()));
57+
},
58+
)?;
59+
PResult::Ok(items)
60+
};
61+
match call() {
62+
Err(mut err) => {
63+
err.emit();
64+
return DummyResult::any(sp);
65+
}
66+
Ok(items) => Box::new(CfgMatchOutput(items)),
67+
}
68+
}
69+
70+
fn iter<'io, 'session>(
71+
cx: &mut ExtCtxt<'session>,
72+
items: &mut [P<Item>],
73+
items_offsets: &'io [(TokenStream, usize)],
74+
no: &mut Vec<&'io TokenStream>,
75+
sp: Span,
76+
mut cb: impl FnMut(
77+
&mut ExtCtxt<'session>,
78+
&mut Vec<&'io TokenStream>,
79+
&'io TokenStream,
80+
&mut [P<Item>],
81+
),
82+
mut cb_last: impl FnMut(
83+
&mut ExtCtxt<'session>,
84+
&mut Vec<&'io TokenStream>,
85+
&'io TokenStream,
86+
&mut [P<Item>],
87+
),
88+
) -> PResult<'session, ()> {
89+
match items_offsets {
90+
[] | [_] => return Err(cx.sess.create_err(errors::CfgMatchMeaninglessArms { span: sp })),
91+
[first, rest @ .., last] => {
92+
let mut offset = first.1;
93+
let mut prev_offset = 0;
94+
cb(cx, no, &first.0, items.get_mut(prev_offset..offset).unwrap_or_default());
95+
for elem in rest {
96+
prev_offset = offset;
97+
offset = elem.1;
98+
cb(cx, no, &elem.0, items.get_mut(prev_offset..offset).unwrap_or_default());
99+
}
100+
prev_offset = offset;
101+
offset = last.1;
102+
cb_last(cx, no, &last.0, items.get_mut(prev_offset..offset).unwrap_or_default());
103+
}
104+
}
105+
Ok(())
106+
}
107+
108+
// #[cfg(all(** YES **, not(any(** NO **, ** NO **, ..))))]
109+
fn mk_cfg(
110+
cx: &mut ExtCtxt<'_>,
111+
no: &[&TokenStream],
112+
sp: Span,
113+
yes: Option<&TokenStream>,
114+
) -> Attribute {
115+
let mut any_tokens = TokenStream::new(Vec::with_capacity(4));
116+
if let &[first, ref rest @ ..] = no {
117+
any_tokens.push_stream(first.clone());
118+
for elem in rest.iter().copied() {
119+
any_tokens.push_tree(TokenTree::token_alone(token::Comma, sp));
120+
any_tokens.push_stream(elem.clone());
121+
}
122+
}
123+
124+
let mut not_tokens = TokenStream::new(Vec::with_capacity(2));
125+
not_tokens.push_tree(TokenTree::token_alone(token::Ident(sym::any, false), sp));
126+
not_tokens.push_tree(TokenTree::Delimited(
127+
DelimSpan::from_single(sp),
128+
token::Delimiter::Parenthesis,
129+
any_tokens,
130+
));
131+
132+
let mut all_tokens = TokenStream::new(Vec::with_capacity(4));
133+
if let Some(elem) = yes {
134+
all_tokens.push_stream(elem.clone());
135+
all_tokens.push_tree(TokenTree::token_alone(token::Comma, sp));
136+
}
137+
all_tokens.push_tree(TokenTree::token_alone(token::Ident(sym::not, false), sp));
138+
all_tokens.push_tree(TokenTree::Delimited(
139+
DelimSpan::from_single(sp),
140+
token::Delimiter::Parenthesis,
141+
not_tokens,
142+
));
143+
144+
let mut tokens = TokenStream::new(Vec::with_capacity(2));
145+
tokens.push_tree(TokenTree::token_alone(token::Ident(sym::all, false), sp));
146+
tokens.push_tree(TokenTree::Delimited(
147+
DelimSpan::from_single(sp),
148+
token::Delimiter::Parenthesis,
149+
all_tokens,
150+
));
151+
152+
mk_attr(
153+
&cx.sess.parse_sess.attr_id_generator,
154+
AttrStyle::Outer,
155+
Path::from_ident(Ident::new(sym::cfg, sp)),
156+
AttrArgs::Delimited(DelimArgs {
157+
dspan: DelimSpan::from_single(sp),
158+
delim: token::Delimiter::Parenthesis,
159+
tokens,
160+
}),
161+
sp,
162+
)
163+
}
164+
165+
fn parse<'session>(
166+
cx: &mut ExtCtxt<'session>,
167+
tts: TokenStream,
168+
) -> PResult<'session, (bool, Vec<P<Item>>, Vec<(TokenStream, usize)>)> {
169+
let mut parser = cx.new_parser_from_tts(tts);
170+
let mut does_not_have_wildcard = true;
171+
let mut items = Vec::with_capacity(4);
172+
let mut items_offsets = Vec::with_capacity(4);
173+
loop {
174+
match parse_cfg_arm(&mut items, &mut parser)? {
175+
None => break,
176+
Some(attr) => {
177+
items_offsets.push((attr, items.len()));
178+
}
179+
}
180+
}
181+
if parser.token != token::Eof {
182+
parse_wildcard_arm(&mut items, &mut parser)?;
183+
does_not_have_wildcard = false;
184+
items_offsets.push((TokenStream::new(vec![]), items.len()));
185+
}
186+
if parser.token != token::Eof {
187+
return parser.unexpected();
188+
}
189+
Ok((does_not_have_wildcard, items, items_offsets))
190+
}
191+
192+
fn parse_arbitrary_arm_block<'session>(
193+
items: &mut Vec<P<Item>>,
194+
mandatory_comma: bool,
195+
parser: &mut Parser<'session>,
196+
) -> PResult<'session, ()> {
197+
if parser.eat(&token::OpenDelim(token::Delimiter::Brace)) {
198+
loop {
199+
let item = match parser.parse_item(ForceCollect::No) {
200+
Ok(Some(elem)) => elem,
201+
_ => break,
202+
};
203+
items.push(item);
204+
}
205+
parser.expect(&token::CloseDelim(token::Delimiter::Brace))?;
206+
} else {
207+
let Ok(Some(item)) = parser.parse_item(ForceCollect::No) else {
208+
return parser.unexpected();
209+
};
210+
if !matches!(item.kind, ItemKind::Fn(_)) {
211+
return Err(parser
212+
.sess
213+
.create_err(errors::CfgMatchBadSingleArm { span: parser.token.span }));
214+
}
215+
let has_comma = parser.eat(&token::Comma);
216+
if mandatory_comma && !has_comma {
217+
return Err(parser
218+
.sess
219+
.create_err(errors::CfgMatchMissingComma { span: parser.token.span }));
220+
}
221+
items.push(item);
222+
}
223+
Ok(())
224+
}
225+
226+
fn parse_cfg_arm<'session>(
227+
items: &mut Vec<P<Item>>,
228+
parser: &mut Parser<'session>,
229+
) -> PResult<'session, Option<TokenStream>> {
230+
if !parser.eat_keyword(sym::cfg) {
231+
return Ok(None);
232+
}
233+
let TokenTree::Delimited(_, delim, tokens) = parser.parse_token_tree() else {
234+
return parser.unexpected();
235+
};
236+
if delim != token::Delimiter::Parenthesis || !parser.eat(&token::FatArrow) {
237+
return Err(parser.sess.create_err(errors::CfgMatchBadArm { span: parser.token.span }));
238+
}
239+
parse_arbitrary_arm_block(items, true, parser)?;
240+
Ok(Some(tokens))
241+
}
242+
243+
fn parse_wildcard_arm<'session>(
244+
items: &mut Vec<P<Item>>,
245+
parser: &mut Parser<'session>,
246+
) -> PResult<'session, ()> {
247+
if !parser.eat_keyword(kw::Underscore) || !parser.eat(&token::FatArrow) {
248+
return Err(parser
249+
.sess
250+
.create_err(errors::CfgMatchBadWildcard { span: parser.token.span }));
251+
}
252+
parse_arbitrary_arm_block(items, false, parser)?;
253+
Ok(())
254+
}
255+
256+
struct CfgMatchOutput(Vec<P<Item>>);
257+
258+
impl MacResult for CfgMatchOutput {
259+
fn make_impl_items(self: Box<Self>) -> Option<SmallVec<[P<AssocItem>; 1]>> {
260+
let mut rslt = SmallVec::with_capacity(self.0.len());
261+
rslt.extend(self.0.into_iter().filter_map(|item| {
262+
let Item { attrs, id, span, vis, ident, kind, tokens } = item.into_inner();
263+
let ItemKind::Fn(fun) = kind else {
264+
return None;
265+
};
266+
Some(P(Item { attrs, id, ident, kind: AssocItemKind::Fn(fun), span, tokens, vis }))
267+
}));
268+
Some(rslt)
269+
}
270+
271+
fn make_items(self: Box<Self>) -> Option<SmallVec<[P<Item>; 1]>> {
272+
Some(<_>::from(self.0))
273+
}
274+
275+
fn make_stmts(self: Box<Self>) -> Option<SmallVec<[Stmt; 1]>> {
276+
let mut rslt = SmallVec::with_capacity(self.0.len());
277+
rslt.extend(self.0.into_iter().map(|item| {
278+
let id = item.id;
279+
let span = item.span;
280+
Stmt { id, kind: StmtKind::Item(item), span }
281+
}));
282+
Some(rslt)
283+
}
284+
}

0 commit comments

Comments
 (0)