Skip to content

Commit 5c92d8f

Browse files
committed
Extend cfg_if! support to cfg_match!
1 parent ee329d3 commit 5c92d8f

4 files changed

Lines changed: 184 additions & 0 deletions

File tree

src/modules.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,25 @@ impl<'ast, 'psess, 'c> ModResolver<'ast, 'psess> {
167167
Ok(())
168168
}
169169

170+
fn visit_cfg_match(&mut self, item: Cow<'ast, ast::Item>) -> Result<(), ModuleResolutionError> {
171+
let mut visitor = visitor::CfgMatchVisitor::new(self.psess);
172+
visitor.visit_item(&item);
173+
for module_item in visitor.mods() {
174+
if let ast::ItemKind::Mod(_, ref sub_mod_kind) = module_item.item.kind {
175+
self.visit_sub_mod(
176+
&module_item.item,
177+
Module::new(
178+
module_item.item.span,
179+
Some(Cow::Owned(sub_mod_kind.clone())),
180+
Cow::Owned(ThinVec::new()),
181+
Cow::Owned(ast::AttrVec::new()),
182+
),
183+
)?;
184+
}
185+
}
186+
Ok(())
187+
}
188+
170189
/// Visit modules defined inside macro calls.
171190
fn visit_mod_outside_ast(
172191
&mut self,
@@ -178,6 +197,11 @@ impl<'ast, 'psess, 'c> ModResolver<'ast, 'psess> {
178197
continue;
179198
}
180199

200+
if is_cfg_match(&item) {
201+
self.visit_cfg_match(Cow::Owned(item.into_inner()))?;
202+
continue;
203+
}
204+
181205
if let ast::ItemKind::Mod(_, ref sub_mod_kind) = item.kind {
182206
let span = item.span;
183207
self.visit_sub_mod(
@@ -204,6 +228,10 @@ impl<'ast, 'psess, 'c> ModResolver<'ast, 'psess> {
204228
self.visit_cfg_if(Cow::Borrowed(item))?;
205229
}
206230

231+
if is_cfg_match(item) {
232+
self.visit_cfg_match(Cow::Borrowed(item))?;
233+
}
234+
207235
if let ast::ItemKind::Mod(_, ref sub_mod_kind) = item.kind {
208236
let span = item.span;
209237
self.visit_sub_mod(
@@ -575,3 +603,17 @@ fn is_cfg_if(item: &ast::Item) -> bool {
575603
_ => false,
576604
}
577605
}
606+
607+
fn is_cfg_match(item: &ast::Item) -> bool {
608+
match item.kind {
609+
ast::ItemKind::MacCall(ref mac) => {
610+
if let Some(last_segment) = mac.path.segments.last() {
611+
if last_segment.ident.name == Symbol::intern("cfg_match") {
612+
return true;
613+
}
614+
}
615+
false
616+
}
617+
_ => false,
618+
}
619+
}

src/modules/visitor.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use tracing::debug;
55

66
use crate::attr::MetaVisitor;
77
use crate::parse::macros::cfg_if::parse_cfg_if;
8+
use crate::parse::macros::cfg_match::parse_cfg_match;
89
use crate::parse::session::ParseSess;
910

1011
pub(crate) struct ModItem {
@@ -71,6 +72,65 @@ impl<'a, 'ast: 'a> CfgIfVisitor<'a> {
7172
}
7273
}
7374

75+
/// Traverse `cfg_match!` macro and fetch modules.
76+
pub(crate) struct CfgMatchVisitor<'a> {
77+
psess: &'a ParseSess,
78+
mods: Vec<ModItem>,
79+
}
80+
81+
impl<'a> CfgMatchVisitor<'a> {
82+
pub(crate) fn new(psess: &'a ParseSess) -> CfgMatchVisitor<'a> {
83+
CfgMatchVisitor {
84+
mods: vec![],
85+
psess,
86+
}
87+
}
88+
89+
pub(crate) fn mods(self) -> Vec<ModItem> {
90+
self.mods
91+
}
92+
}
93+
94+
impl<'a, 'ast: 'a> Visitor<'ast> for CfgMatchVisitor<'a> {
95+
fn visit_mac_call(&mut self, mac: &'ast ast::MacCall) {
96+
match self.visit_mac_inner(mac) {
97+
Ok(()) => (),
98+
Err(e) => debug!("{}", e),
99+
}
100+
}
101+
}
102+
103+
impl<'a, 'ast: 'a> CfgMatchVisitor<'a> {
104+
fn visit_mac_inner(&mut self, mac: &'ast ast::MacCall) -> Result<(), &'static str> {
105+
// Support both:
106+
// ```
107+
// std::cfg_match! {..}
108+
// core::cfg_match! {..}
109+
// ```
110+
// And:
111+
// ```
112+
// use std::cfg_match;
113+
// cfg_match! {..}
114+
// ```
115+
match mac.path.segments.last() {
116+
Some(last_segment) => {
117+
if last_segment.ident.name != Symbol::intern("cfg_match") {
118+
return Err("Expected cfg_match");
119+
}
120+
}
121+
None => {
122+
return Err("Expected cfg_match");
123+
}
124+
};
125+
126+
let items = parse_cfg_match(self.psess, mac)?;
127+
self.mods
128+
.append(&mut items.into_iter().map(|item| ModItem { item }).collect());
129+
130+
Ok(())
131+
}
132+
}
133+
74134
/// Extracts `path = "foo.rs"` from attributes.
75135
#[derive(Default)]
76136
pub(crate) struct PathVisitor {

src/parse/macros/cfg_match.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use std::panic::{AssertUnwindSafe, catch_unwind};
2+
3+
use rustc_ast::ast;
4+
use rustc_ast::token::{Delimiter, TokenKind};
5+
use rustc_parse::exp;
6+
use rustc_parse::parser::ForceCollect;
7+
use rustc_span::symbol::kw;
8+
9+
use crate::parse::macros::build_stream_parser;
10+
use crate::parse::session::ParseSess;
11+
12+
pub(crate) fn parse_cfg_match<'a>(
13+
psess: &'a ParseSess,
14+
mac: &'a ast::MacCall,
15+
) -> Result<Vec<ast::Item>, &'static str> {
16+
match catch_unwind(AssertUnwindSafe(|| parse_cfg_match_inner(psess, mac))) {
17+
Ok(Ok(items)) => Ok(items),
18+
Ok(err @ Err(_)) => err,
19+
Err(..) => Err("failed to parse cfg_match!"),
20+
}
21+
}
22+
23+
fn parse_cfg_match_inner<'a>(
24+
psess: &'a ParseSess,
25+
mac: &'a ast::MacCall,
26+
) -> Result<Vec<ast::Item>, &'static str> {
27+
let ts = mac.args.tokens.clone();
28+
let mut parser = build_stream_parser(psess.inner(), ts);
29+
30+
if parser.token == TokenKind::OpenDelim(Delimiter::Brace) {
31+
return Err("Expression position cfg_match! not yet supported");
32+
}
33+
34+
let mut items = vec![];
35+
36+
while parser.token.kind != TokenKind::Eof {
37+
if !parser.eat_keyword(exp!(Underscore)) {
38+
parser.parse_attr_item(ForceCollect::No).map_err(|e| {
39+
e.cancel();
40+
"Failed to parse attr item"
41+
})?;
42+
}
43+
44+
if !parser.eat(exp!(FatArrow)) {
45+
return Err("Expected a fat arrow");
46+
}
47+
48+
if !parser.eat(exp!(OpenBrace)) {
49+
return Err("Expected an opening brace");
50+
}
51+
52+
while parser.token != TokenKind::CloseDelim(Delimiter::Brace)
53+
&& parser.token.kind != TokenKind::Eof
54+
{
55+
let item = match parser.parse_item(ForceCollect::No) {
56+
Ok(Some(item_ptr)) => item_ptr.into_inner(),
57+
Ok(None) => continue,
58+
Err(err) => {
59+
err.cancel();
60+
parser.psess.dcx().reset_err_count();
61+
return Err(
62+
"Expected item inside cfg_match block, but failed to parse it as an item",
63+
);
64+
}
65+
};
66+
if let ast::ItemKind::Mod(..) = item.kind {
67+
items.push(item);
68+
}
69+
}
70+
71+
if !parser.eat(exp!(CloseBrace)) {
72+
return Err("Expected a closing brace");
73+
}
74+
75+
if parser.eat(exp!(Eof)) {
76+
break;
77+
}
78+
}
79+
80+
Ok(items)
81+
}

src/parse/macros/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::rewrite::RewriteContext;
1111

1212
pub(crate) mod asm;
1313
pub(crate) mod cfg_if;
14+
pub(crate) mod cfg_match;
1415
pub(crate) mod lazy_static;
1516

1617
fn build_stream_parser<'a>(psess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> {

0 commit comments

Comments
 (0)