Skip to content

Commit bedef9b

Browse files
authored
Try to discover mod items in cfg_match! arms (#6522)
1 parent 5688caa commit bedef9b

17 files changed

+274
-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> {

src/test/mod.rs

+41
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ const FILE_SKIP_LIST: &[&str] = &[
4242
"issue-3253/foo.rs",
4343
"issue-3253/bar.rs",
4444
"issue-3253/paths",
45+
// This directory is directly tested by format_files_find_new_files_via_cfg_match
46+
"cfg_match",
4547
// These files and directory are a part of modules defined inside `cfg_attr(..)`.
4648
"cfg_mod/dir",
4749
"cfg_mod/bar.rs",
@@ -468,6 +470,45 @@ fn format_files_find_new_files_via_cfg_if() {
468470
});
469471
}
470472

473+
#[test]
474+
fn format_files_find_new_files_via_cfg_match() {
475+
init_log();
476+
run_test_with(&TestSetting::default(), || {
477+
// We load these two files into the same session to test cfg_match!
478+
// transparent mod discovery, and to ensure that it does not suffer
479+
// from a similar issue as cfg_if! support did with issue-4656.
480+
let files = vec![
481+
Path::new("tests/source/cfg_match/lib2.rs"),
482+
Path::new("tests/source/cfg_match/lib.rs"),
483+
];
484+
485+
let config = Config::default();
486+
let mut session = Session::<io::Stdout>::new(config, None);
487+
488+
let mut write_result = HashMap::new();
489+
for file in files {
490+
assert!(file.exists());
491+
let result = session.format(Input::File(file.into())).unwrap();
492+
assert!(!session.has_formatting_errors());
493+
assert!(!result.has_warnings());
494+
let mut source_file = SourceFile::new();
495+
mem::swap(&mut session.source_file, &mut source_file);
496+
497+
for (filename, text) in source_file {
498+
if let FileName::Real(ref filename) = filename {
499+
write_result.insert(filename.to_owned(), text);
500+
}
501+
}
502+
}
503+
assert_eq!(
504+
6,
505+
write_result.len(),
506+
"Should have uncovered an extra file (format_me_please_x.rs) via lib.rs"
507+
);
508+
assert!(handle_result(write_result, None).is_ok());
509+
});
510+
}
511+
471512
#[test]
472513
fn stdin_formatting_smoke_test() {
473514
init_log();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
pub fn format_me_please_1( ) { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
pub fn format_me_please_2( ) { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
pub fn format_me_please_3( ) { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
pub fn format_me_please_4( ) { }

tests/source/cfg_match/lib.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![feature(cfg_match)]
2+
3+
std::cfg_match! {
4+
test => {
5+
mod format_me_please_1;
6+
}
7+
target_family = "unix" => {
8+
mod format_me_please_2;
9+
}
10+
cfg(target_pointer_width = "32") => {
11+
mod format_me_please_3;
12+
}
13+
_ => {
14+
mod format_me_please_4;
15+
}
16+
}

tests/source/cfg_match/lib2.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
its_a_macro! {
2+
// Contents
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub fn format_me_please_1() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub fn format_me_please_2() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub fn format_me_please_3() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub fn format_me_please_4() {}

tests/target/cfg_match/lib.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![feature(cfg_match)]
2+
3+
std::cfg_match! {
4+
test => {
5+
mod format_me_please_1;
6+
}
7+
target_family = "unix" => {
8+
mod format_me_please_2;
9+
}
10+
cfg(target_pointer_width = "32") => {
11+
mod format_me_please_3;
12+
}
13+
_ => {
14+
mod format_me_please_4;
15+
}
16+
}

tests/target/cfg_match/lib2.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
its_a_macro! {
2+
// Contents
3+
}

0 commit comments

Comments
 (0)