Skip to content

Commit a0a374b

Browse files
CAD97ytmimi
authored andcommitted
Extend cfg_if! support to cfg_match!
1 parent 5688caa commit a0a374b

File tree

4 files changed

+183
-0
lines changed

4 files changed

+183
-0
lines changed

src/modules.rs

+42
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

+60
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

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

src/parse/macros/mod.rs

+1
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)