use std::panic::{catch_unwind, AssertUnwindSafe};
use rustc_ast::ast;
use rustc_ast::token::{Delimiter, TokenKind};
use rustc_parse::parser::ForceCollect;
use rustc_span::symbol::kw;
use crate::parse::macros::build_stream_parser;
use crate::parse::session::ParseSess;
pub(crate) fn parse_cfg_if<'a>(
sess: &'a ParseSess,
mac: &'a ast::MacCall,
) -> Result<Vec<ast::Item>, &'static str> {
match catch_unwind(AssertUnwindSafe(|| parse_cfg_if_inner(sess, mac))) {
Ok(Ok(items)) => Ok(items),
Ok(err @ Err(_)) => err,
Err(..) => Err("failed to parse cfg_if!"),
}
}
fn parse_cfg_if_inner<'a>(
sess: &'a ParseSess,
mac: &'a ast::MacCall,
) -> Result<Vec<ast::Item>, &'static str> {
let ts = mac.args.tokens.clone();
let mut parser = build_stream_parser(sess.inner(), ts);
let mut items = vec![];
let mut process_if_cfg = true;
while parser.token.kind != TokenKind::Eof {
if process_if_cfg {
if !parser.eat_keyword(kw::If) {
return Err("Expected `if`");
}
if !matches!(parser.token.kind, TokenKind::Pound) {
return Err("Failed to parse attributes");
}
parser
.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
.map_err(|e| {
e.cancel();
"Failed to parse attributes"
})?;
}
if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) {
return Err("Expected an opening brace");
}
while parser.token != TokenKind::CloseDelim(Delimiter::Brace)
&& parser.token.kind != TokenKind::Eof
{
let item = match parser.parse_item(ForceCollect::No) {
Ok(Some(item_ptr)) => item_ptr.into_inner(),
Ok(None) => continue,
Err(err) => {
err.cancel();
parser.sess.span_diagnostic.reset_err_count();
return Err(
"Expected item inside cfg_if block, but failed to parse it as an item",
);
}
};
if let ast::ItemKind::Mod(..) = item.kind {
items.push(item);
}
}
if !parser.eat(&TokenKind::CloseDelim(Delimiter::Brace)) {
return Err("Expected a closing brace");
}
if parser.eat(&TokenKind::Eof) {
break;
}
if !parser.eat_keyword(kw::Else) {
return Err("Expected `else`");
}
process_if_cfg = parser.token.is_keyword(kw::If);
}
Ok(items)
}