-
Notifications
You must be signed in to change notification settings - Fork 12.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
613 additions
and
162 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
use crate::errors; | ||
use rustc_ast::{ | ||
attr::mk_attr, | ||
ptr::P, | ||
token, | ||
tokenstream::{DelimSpan, TokenStream, TokenTree}, | ||
AssocItem, AssocItemKind, AttrArgs, AttrStyle, Attribute, DelimArgs, Item, ItemKind, Path, | ||
Stmt, StmtKind, | ||
}; | ||
use rustc_errors::PResult; | ||
use rustc_expand::base::{DummyResult, ExtCtxt, MacResult}; | ||
use rustc_parse::parser::{ForceCollect, Parser}; | ||
use rustc_span::{ | ||
symbol::{kw, sym, Ident}, | ||
Span, | ||
}; | ||
use smallvec::SmallVec; | ||
|
||
// ```rust | ||
// cfg_match! { | ||
// cfg(unix) => { fn foo() -> i32 { 1 } }, | ||
// _ => { fn foo() -> i32 { 2 } }, | ||
// } | ||
// ``` | ||
// | ||
// The above `cfg_match!` is expanded to the following elements: | ||
// | ||
// ```rust | ||
// #[cfg(all(unix, not(any())))] | ||
// fn foo() -> i32 { 1 } | ||
// | ||
// #[cfg(all(not(any(unix))))] | ||
// fn foo() -> i32 { 2 } | ||
// ``` | ||
pub fn expand_cfg_match( | ||
cx: &mut ExtCtxt<'_>, | ||
sp: Span, | ||
tts: TokenStream, | ||
) -> Box<dyn MacResult + 'static> { | ||
let (does_not_have_wildcard, mut items, mut items_offsets) = match parse(cx, tts) { | ||
Err(mut err) => { | ||
err.emit(); | ||
return DummyResult::any(sp); | ||
} | ||
Ok(assert) => assert, | ||
}; | ||
let mut no = Vec::with_capacity(items_offsets.len()); | ||
iter( | ||
(cx, &mut no), | ||
&mut items, | ||
&mut items_offsets, | ||
|(local_cx, local_no), yes, items| { | ||
let attr = mk_cfg(local_cx, local_no, sp, Some(yes)); | ||
items.iter_mut().for_each(|item| item.attrs.push(attr.clone())); | ||
local_no.push(yes); | ||
}, | ||
|(local_cx, local_no), yes, items| { | ||
let attr = mk_cfg(local_cx, local_no, sp, does_not_have_wildcard.then_some(yes)); | ||
items.iter_mut().for_each(|item| item.attrs.push(attr.clone())); | ||
}, | ||
); | ||
Box::new(CfgMatchOutput(items)) | ||
} | ||
|
||
fn iter<'io, A>( | ||
mut aux: A, | ||
items: &mut [P<Item>], | ||
items_offsets: &'io [(TokenStream, usize)], | ||
mut cb: impl FnMut(&mut A, &'io TokenStream, &mut [P<Item>]), | ||
mut cb_last: impl FnMut(&mut A, &'io TokenStream, &mut [P<Item>]), | ||
) { | ||
match items_offsets { | ||
[] => return, | ||
[first] => { | ||
cb_last(&mut aux, &first.0, items.get_mut(..first.1).unwrap_or_default()); | ||
} | ||
[first, rest @ .., last] => { | ||
let mut offset = first.1; | ||
let mut prev_offset = 0; | ||
cb(&mut aux, &first.0, items.get_mut(prev_offset..offset).unwrap_or_default()); | ||
for elem in rest { | ||
prev_offset = offset; | ||
offset = elem.1; | ||
cb(&mut aux, &elem.0, items.get_mut(prev_offset..offset).unwrap_or_default()); | ||
} | ||
prev_offset = offset; | ||
offset = last.1; | ||
cb_last(&mut aux, &last.0, items.get_mut(prev_offset..offset).unwrap_or_default()); | ||
} | ||
} | ||
} | ||
|
||
// #[cfg(all(** YES **, not(any(** NO **, ** NO **, ..))))] | ||
fn mk_cfg( | ||
cx: &mut ExtCtxt<'_>, | ||
no: &[&TokenStream], | ||
sp: Span, | ||
yes: Option<&TokenStream>, | ||
) -> Attribute { | ||
let mut any_tokens = TokenStream::new(Vec::with_capacity(4)); | ||
if let &[first, ref rest @ ..] = no { | ||
any_tokens.push_stream(first.clone()); | ||
for elem in rest.iter().copied() { | ||
any_tokens.push_tree(TokenTree::token_alone(token::Comma, sp)); | ||
any_tokens.push_stream(elem.clone()); | ||
} | ||
} | ||
|
||
let mut not_tokens = TokenStream::new(Vec::with_capacity(2)); | ||
not_tokens.push_tree(TokenTree::token_alone(token::Ident(sym::any, false), sp)); | ||
not_tokens.push_tree(TokenTree::Delimited( | ||
DelimSpan::from_single(sp), | ||
token::Delimiter::Parenthesis, | ||
any_tokens, | ||
)); | ||
|
||
let mut all_tokens = TokenStream::new(Vec::with_capacity(4)); | ||
if let Some(elem) = yes { | ||
all_tokens.push_stream(elem.clone()); | ||
all_tokens.push_tree(TokenTree::token_alone(token::Comma, sp)); | ||
} | ||
all_tokens.push_tree(TokenTree::token_alone(token::Ident(sym::not, false), sp)); | ||
all_tokens.push_tree(TokenTree::Delimited( | ||
DelimSpan::from_single(sp), | ||
token::Delimiter::Parenthesis, | ||
not_tokens, | ||
)); | ||
|
||
let mut tokens = TokenStream::new(Vec::with_capacity(2)); | ||
tokens.push_tree(TokenTree::token_alone(token::Ident(sym::all, false), sp)); | ||
tokens.push_tree(TokenTree::Delimited( | ||
DelimSpan::from_single(sp), | ||
token::Delimiter::Parenthesis, | ||
all_tokens, | ||
)); | ||
|
||
mk_attr( | ||
&cx.sess.parse_sess.attr_id_generator, | ||
AttrStyle::Outer, | ||
Path::from_ident(Ident::new(sym::cfg, sp)), | ||
AttrArgs::Delimited(DelimArgs { | ||
dspan: DelimSpan::from_single(sp), | ||
delim: token::Delimiter::Parenthesis, | ||
tokens, | ||
}), | ||
sp, | ||
) | ||
} | ||
|
||
fn parse<'a>( | ||
cx: &mut ExtCtxt<'a>, | ||
tts: TokenStream, | ||
) -> PResult<'a, (bool, Vec<P<Item>>, Vec<(TokenStream, usize)>)> { | ||
let mut parser = cx.new_parser_from_tts(tts); | ||
if parser.token == token::Eof { | ||
return parser.unexpected(); | ||
} | ||
let mut does_not_have_wildcard = true; | ||
let mut items = Vec::with_capacity(4); | ||
let mut items_offsets = Vec::with_capacity(4); | ||
loop { | ||
match parse_cfg_arm(&mut items, &mut parser)? { | ||
None => break, | ||
Some(attr) => { | ||
items_offsets.push((attr, items.len())); | ||
} | ||
} | ||
} | ||
if parser.token != token::Eof { | ||
parse_wildcard_arm(&mut items, &mut parser)?; | ||
does_not_have_wildcard = false; | ||
items_offsets.push((TokenStream::new(vec![]), items.len())); | ||
} | ||
if parser.token != token::Eof { | ||
return parser.unexpected(); | ||
} | ||
Ok((does_not_have_wildcard, items, items_offsets)) | ||
} | ||
|
||
fn parse_arbitrary_arm_block<'a>( | ||
items: &mut Vec<P<Item>>, | ||
parser: &mut Parser<'a>, | ||
) -> PResult<'a, ()> { | ||
if parser.eat(&token::OpenDelim(token::Delimiter::Brace)) { | ||
loop { | ||
let item = match parser.parse_item(ForceCollect::No) { | ||
Ok(Some(elem)) => elem, | ||
_ => break, | ||
}; | ||
items.push(item); | ||
} | ||
parser.expect(&token::CloseDelim(token::Delimiter::Brace))?; | ||
} else { | ||
let Ok(Some(item)) = parser.parse_item(ForceCollect::No) else { | ||
return parser.unexpected(); | ||
}; | ||
if !parser.eat(&token::Comma) { | ||
return Err(parser | ||
.sess | ||
.create_err(errors::CfgMatchMissingComma { span: parser.token.span })); | ||
} | ||
items.push(item); | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn parse_cfg_arm<'a>( | ||
items: &mut Vec<P<Item>>, | ||
parser: &mut Parser<'a>, | ||
) -> PResult<'a, Option<TokenStream>> { | ||
if !parser.eat_keyword(sym::cfg) { | ||
return Ok(None); | ||
} | ||
let TokenTree::Delimited(_, delim, tokens) = parser.parse_token_tree() else { | ||
return parser.unexpected(); | ||
}; | ||
if delim != token::Delimiter::Parenthesis || !parser.eat(&token::FatArrow) { | ||
return Err(parser | ||
.sess | ||
.create_err(errors::CfgMatchBadArm { span: parser.token.span })); | ||
} | ||
parse_arbitrary_arm_block(items, parser)?; | ||
Ok(Some(tokens)) | ||
} | ||
|
||
fn parse_wildcard_arm<'a>(items: &mut Vec<P<Item>>, parser: &mut Parser<'a>) -> PResult<'a, ()> { | ||
if !parser.eat_keyword(kw::Underscore) || !parser.eat(&token::FatArrow) { | ||
return Err(parser | ||
.sess | ||
.create_err(errors::CfgMatchBadWildcard { span: parser.token.span })); | ||
} | ||
parse_arbitrary_arm_block(items, parser)?; | ||
Ok(()) | ||
} | ||
|
||
struct CfgMatchOutput(Vec<P<Item>>); | ||
|
||
impl MacResult for CfgMatchOutput { | ||
fn make_impl_items(self: Box<Self>) -> Option<SmallVec<[P<AssocItem>; 1]>> { | ||
let mut rslt = SmallVec::with_capacity(self.0.len()); | ||
rslt.extend(self.0.into_iter().filter_map(|item| { | ||
let Item { attrs, id, span, vis, ident, kind, tokens } = item.into_inner(); | ||
let ItemKind::Fn(fun) = kind else { | ||
return None; | ||
}; | ||
Some(P(Item { attrs, id, ident, kind: AssocItemKind::Fn(fun), span, tokens, vis })) | ||
})); | ||
Some(rslt) | ||
} | ||
|
||
fn make_items(self: Box<Self>) -> Option<SmallVec<[P<Item>; 1]>> { | ||
Some(<_>::from(self.0)) | ||
} | ||
|
||
fn make_stmts(self: Box<Self>) -> Option<SmallVec<[Stmt; 1]>> { | ||
let mut rslt = SmallVec::with_capacity(self.0.len()); | ||
rslt.extend(self.0.into_iter().map(|item| { | ||
let id = item.id; | ||
let span = item.span; | ||
Stmt { id, kind: StmtKind::Item(item), span } | ||
})); | ||
Some(rslt) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.