From a0a374b4333a90f31331c6d7a1354bfdcb17755a Mon Sep 17 00:00:00 2001
From: Christopher Durham <cad97@cad97.com>
Date: Wed, 26 Mar 2025 16:22:40 -0400
Subject: [PATCH 1/3] Extend cfg_if! support to cfg_match!

---
 src/modules.rs                | 42 ++++++++++++++++++
 src/modules/visitor.rs        | 60 ++++++++++++++++++++++++++
 src/parse/macros/cfg_match.rs | 80 +++++++++++++++++++++++++++++++++++
 src/parse/macros/mod.rs       |  1 +
 4 files changed, 183 insertions(+)
 create mode 100644 src/parse/macros/cfg_match.rs

diff --git a/src/modules.rs b/src/modules.rs
index bc5a6d3e704..4270f692910 100644
--- a/src/modules.rs
+++ b/src/modules.rs
@@ -167,6 +167,25 @@ impl<'ast, 'psess, 'c> ModResolver<'ast, 'psess> {
         Ok(())
     }
 
+    fn visit_cfg_match(&mut self, item: Cow<'ast, ast::Item>) -> Result<(), ModuleResolutionError> {
+        let mut visitor = visitor::CfgMatchVisitor::new(self.psess);
+        visitor.visit_item(&item);
+        for module_item in visitor.mods() {
+            if let ast::ItemKind::Mod(_, _, ref sub_mod_kind) = module_item.item.kind {
+                self.visit_sub_mod(
+                    &module_item.item,
+                    Module::new(
+                        module_item.item.span,
+                        Some(Cow::Owned(sub_mod_kind.clone())),
+                        Cow::Owned(ThinVec::new()),
+                        Cow::Owned(ast::AttrVec::new()),
+                    ),
+                )?;
+            }
+        }
+        Ok(())
+    }
+
     /// Visit modules defined inside macro calls.
     fn visit_mod_outside_ast(
         &mut self,
@@ -178,6 +197,11 @@ impl<'ast, 'psess, 'c> ModResolver<'ast, 'psess> {
                 continue;
             }
 
+            if is_cfg_match(&item) {
+                self.visit_cfg_match(Cow::Owned(item.into_inner()))?;
+                continue;
+            }
+
             if let ast::ItemKind::Mod(_, _, ref sub_mod_kind) = item.kind {
                 let span = item.span;
                 self.visit_sub_mod(
@@ -204,6 +228,10 @@ impl<'ast, 'psess, 'c> ModResolver<'ast, 'psess> {
                 self.visit_cfg_if(Cow::Borrowed(item))?;
             }
 
+            if is_cfg_match(item) {
+                self.visit_cfg_match(Cow::Borrowed(item))?;
+            }
+
             if let ast::ItemKind::Mod(_, _, ref sub_mod_kind) = item.kind {
                 let span = item.span;
                 self.visit_sub_mod(
@@ -575,3 +603,17 @@ fn is_cfg_if(item: &ast::Item) -> bool {
         _ => false,
     }
 }
+
+fn is_cfg_match(item: &ast::Item) -> bool {
+    match item.kind {
+        ast::ItemKind::MacCall(ref mac) => {
+            if let Some(last_segment) = mac.path.segments.last() {
+                if last_segment.ident.name == Symbol::intern("cfg_match") {
+                    return true;
+                }
+            }
+            false
+        }
+        _ => false,
+    }
+}
diff --git a/src/modules/visitor.rs b/src/modules/visitor.rs
index 0f26241e3e2..d302a9ede6c 100644
--- a/src/modules/visitor.rs
+++ b/src/modules/visitor.rs
@@ -5,6 +5,7 @@ use tracing::debug;
 
 use crate::attr::MetaVisitor;
 use crate::parse::macros::cfg_if::parse_cfg_if;
+use crate::parse::macros::cfg_match::parse_cfg_match;
 use crate::parse::session::ParseSess;
 
 pub(crate) struct ModItem {
@@ -71,6 +72,65 @@ impl<'a, 'ast: 'a> CfgIfVisitor<'a> {
     }
 }
 
+/// Traverse `cfg_match!` macro and fetch modules.
+pub(crate) struct CfgMatchVisitor<'a> {
+    psess: &'a ParseSess,
+    mods: Vec<ModItem>,
+}
+
+impl<'a> CfgMatchVisitor<'a> {
+    pub(crate) fn new(psess: &'a ParseSess) -> CfgMatchVisitor<'a> {
+        CfgMatchVisitor {
+            mods: vec![],
+            psess,
+        }
+    }
+
+    pub(crate) fn mods(self) -> Vec<ModItem> {
+        self.mods
+    }
+}
+
+impl<'a, 'ast: 'a> Visitor<'ast> for CfgMatchVisitor<'a> {
+    fn visit_mac_call(&mut self, mac: &'ast ast::MacCall) {
+        match self.visit_mac_inner(mac) {
+            Ok(()) => (),
+            Err(e) => debug!("{}", e),
+        }
+    }
+}
+
+impl<'a, 'ast: 'a> CfgMatchVisitor<'a> {
+    fn visit_mac_inner(&mut self, mac: &'ast ast::MacCall) -> Result<(), &'static str> {
+        // Support both:
+        // ```
+        // std::cfg_match! {..}
+        // core::cfg_match! {..}
+        // ```
+        // And:
+        // ```
+        // use std::cfg_match;
+        // cfg_match! {..}
+        // ```
+        match mac.path.segments.last() {
+            Some(last_segment) => {
+                if last_segment.ident.name != Symbol::intern("cfg_match") {
+                    return Err("Expected cfg_match");
+                }
+            }
+            None => {
+                return Err("Expected cfg_match");
+            }
+        };
+
+        let items = parse_cfg_match(self.psess, mac)?;
+        self.mods
+            .append(&mut items.into_iter().map(|item| ModItem { item }).collect());
+
+        Ok(())
+    }
+}
+
 /// Extracts `path = "foo.rs"` from attributes.
 #[derive(Default)]
 pub(crate) struct PathVisitor {
diff --git a/src/parse/macros/cfg_match.rs b/src/parse/macros/cfg_match.rs
new file mode 100644
index 00000000000..87071db2749
--- /dev/null
+++ b/src/parse/macros/cfg_match.rs
@@ -0,0 +1,80 @@
+use std::panic::{AssertUnwindSafe, catch_unwind};
+
+use rustc_ast::ast;
+use rustc_ast::token::{Delimiter, TokenKind};
+use rustc_parse::exp;
+use rustc_parse::parser::ForceCollect;
+
+use crate::parse::macros::build_stream_parser;
+use crate::parse::session::ParseSess;
+
+pub(crate) fn parse_cfg_match<'a>(
+    psess: &'a ParseSess,
+    mac: &'a ast::MacCall,
+) -> Result<Vec<ast::Item>, &'static str> {
+    match catch_unwind(AssertUnwindSafe(|| parse_cfg_match_inner(psess, mac))) {
+        Ok(Ok(items)) => Ok(items),
+        Ok(err @ Err(_)) => err,
+        Err(..) => Err("failed to parse cfg_match!"),
+    }
+}
+
+fn parse_cfg_match_inner<'a>(
+    psess: &'a ParseSess,
+    mac: &'a ast::MacCall,
+) -> Result<Vec<ast::Item>, &'static str> {
+    let ts = mac.args.tokens.clone();
+    let mut parser = build_stream_parser(psess.inner(), ts);
+
+    if parser.token == TokenKind::OpenDelim(Delimiter::Brace) {
+        return Err("Expression position cfg_match! not yet supported");
+    }
+
+    let mut items = vec![];
+
+    while parser.token.kind != TokenKind::Eof {
+        if !parser.eat_keyword(exp!(Underscore)) {
+            parser.parse_attr_item(ForceCollect::No).map_err(|e| {
+                e.cancel();
+                "Failed to parse attr item"
+            })?;
+        }
+
+        if !parser.eat(exp!(FatArrow)) {
+            return Err("Expected a fat arrow");
+        }
+
+        if !parser.eat(exp!(OpenBrace)) {
+            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.psess.dcx().reset_err_count();
+                    return Err(
+                        "Expected item inside cfg_match block, but failed to parse it as an item",
+                    );
+                }
+            };
+            if let ast::ItemKind::Mod(..) = item.kind {
+                items.push(item);
+            }
+        }
+
+        if !parser.eat(exp!(CloseBrace)) {
+            return Err("Expected a closing brace");
+        }
+
+        if parser.eat(exp!(Eof)) {
+            break;
+        }
+    }
+
+    Ok(items)
+}
diff --git a/src/parse/macros/mod.rs b/src/parse/macros/mod.rs
index d7964484b26..8a956faf03b 100644
--- a/src/parse/macros/mod.rs
+++ b/src/parse/macros/mod.rs
@@ -11,6 +11,7 @@ use crate::rewrite::RewriteContext;
 
 pub(crate) mod asm;
 pub(crate) mod cfg_if;
+pub(crate) mod cfg_match;
 pub(crate) mod lazy_static;
 
 fn build_stream_parser<'a>(psess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> {

From 5be7cacb446ff5e4c8afba3aacd5ff5f9a7db1a6 Mon Sep 17 00:00:00 2001
From: Christopher Durham <cad97@cad97.com>
Date: Wed, 26 Mar 2025 16:22:46 -0400
Subject: [PATCH 2/3] Test cfg_match! support

---
 src/test/mod.rs                            | 41 ++++++++++++++++++++++
 tests/source/cfg_match/format_me_please.rs |  2 ++
 tests/source/cfg_match/lib.rs              |  5 +++
 tests/source/cfg_match/lib2.rs             |  3 ++
 tests/target/cfg_match/format_me_please.rs |  1 +
 tests/target/cfg_match/lib.rs              |  5 +++
 tests/target/cfg_match/lib2.rs             |  3 ++
 7 files changed, 60 insertions(+)
 create mode 100644 tests/source/cfg_match/format_me_please.rs
 create mode 100644 tests/source/cfg_match/lib.rs
 create mode 100644 tests/source/cfg_match/lib2.rs
 create mode 100644 tests/target/cfg_match/format_me_please.rs
 create mode 100644 tests/target/cfg_match/lib.rs
 create mode 100644 tests/target/cfg_match/lib2.rs

diff --git a/src/test/mod.rs b/src/test/mod.rs
index d62da08fff8..fa88e3e03c1 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -42,6 +42,8 @@ const FILE_SKIP_LIST: &[&str] = &[
     "issue-3253/foo.rs",
     "issue-3253/bar.rs",
     "issue-3253/paths",
+    // This directory is directly tested by format_files_find_new_files_via_cfg_match
+    "cfg_match",
     // These files and directory are a part of modules defined inside `cfg_attr(..)`.
     "cfg_mod/dir",
     "cfg_mod/bar.rs",
@@ -468,6 +470,45 @@ fn format_files_find_new_files_via_cfg_if() {
     });
 }
 
+#[test]
+fn format_files_find_new_files_via_cfg_match() {
+    init_log();
+    run_test_with(&TestSetting::default(), || {
+        // We load these two files into the same session to test cfg_match!
+        // transparent mod discovery, and to ensure that it does not suffer
+        // from a similar issue as cfg_if! support did with issue-4656.
+        let files = vec![
+            Path::new("tests/source/cfg_match/lib2.rs"),
+            Path::new("tests/source/cfg_match/lib.rs"),
+        ];
+
+        let config = Config::default();
+        let mut session = Session::<io::Stdout>::new(config, None);
+
+        let mut write_result = HashMap::new();
+        for file in files {
+            assert!(file.exists());
+            let result = session.format(Input::File(file.into())).unwrap();
+            assert!(!session.has_formatting_errors());
+            assert!(!result.has_warnings());
+            let mut source_file = SourceFile::new();
+            mem::swap(&mut session.source_file, &mut source_file);
+
+            for (filename, text) in source_file {
+                if let FileName::Real(ref filename) = filename {
+                    write_result.insert(filename.to_owned(), text);
+                }
+            }
+        }
+        assert_eq!(
+            3,
+            write_result.len(),
+            "Should have uncovered an extra file (format_me_please.rs) via lib.rs"
+        );
+        assert!(handle_result(write_result, None).is_ok());
+    });
+}
+
 #[test]
 fn stdin_formatting_smoke_test() {
     init_log();
diff --git a/tests/source/cfg_match/format_me_please.rs b/tests/source/cfg_match/format_me_please.rs
new file mode 100644
index 00000000000..7de75301649
--- /dev/null
+++ b/tests/source/cfg_match/format_me_please.rs
@@ -0,0 +1,2 @@
+
+pub fn     hello(   )  {     }
diff --git a/tests/source/cfg_match/lib.rs b/tests/source/cfg_match/lib.rs
new file mode 100644
index 00000000000..ce64d30f1b1
--- /dev/null
+++ b/tests/source/cfg_match/lib.rs
@@ -0,0 +1,5 @@
+std::cfg_match! {
+    target_family = "unix" => {
+        mod format_me_please;
+    }
+}
diff --git a/tests/source/cfg_match/lib2.rs b/tests/source/cfg_match/lib2.rs
new file mode 100644
index 00000000000..b17fffc58e1
--- /dev/null
+++ b/tests/source/cfg_match/lib2.rs
@@ -0,0 +1,3 @@
+its_a_macro! {
+    // Contents
+}
diff --git a/tests/target/cfg_match/format_me_please.rs b/tests/target/cfg_match/format_me_please.rs
new file mode 100644
index 00000000000..421e195a2fb
--- /dev/null
+++ b/tests/target/cfg_match/format_me_please.rs
@@ -0,0 +1 @@
+pub fn hello() {}
diff --git a/tests/target/cfg_match/lib.rs b/tests/target/cfg_match/lib.rs
new file mode 100644
index 00000000000..ce64d30f1b1
--- /dev/null
+++ b/tests/target/cfg_match/lib.rs
@@ -0,0 +1,5 @@
+std::cfg_match! {
+    target_family = "unix" => {
+        mod format_me_please;
+    }
+}
diff --git a/tests/target/cfg_match/lib2.rs b/tests/target/cfg_match/lib2.rs
new file mode 100644
index 00000000000..b17fffc58e1
--- /dev/null
+++ b/tests/target/cfg_match/lib2.rs
@@ -0,0 +1,3 @@
+its_a_macro! {
+    // Contents
+}

From a5f621b543893c5d6ca114c8fb03e99912cad820 Mon Sep 17 00:00:00 2001
From: Yacin Tmimi <yacintmimi@gmail.com>
Date: Sat, 5 Apr 2025 15:46:30 -0400
Subject: [PATCH 3/3] expand on cfg_match! test cases

---
 src/test/mod.rs                              |  4 ++--
 tests/source/cfg_match/format_me_please.rs   |  2 --
 tests/source/cfg_match/format_me_please_1.rs |  2 ++
 tests/source/cfg_match/format_me_please_2.rs |  2 ++
 tests/source/cfg_match/format_me_please_3.rs |  2 ++
 tests/source/cfg_match/format_me_please_4.rs |  2 ++
 tests/source/cfg_match/lib.rs                | 13 ++++++++++++-
 tests/target/cfg_match/format_me_please.rs   |  1 -
 tests/target/cfg_match/format_me_please_1.rs |  1 +
 tests/target/cfg_match/format_me_please_2.rs |  1 +
 tests/target/cfg_match/format_me_please_3.rs |  1 +
 tests/target/cfg_match/format_me_please_4.rs |  1 +
 tests/target/cfg_match/lib.rs                | 13 ++++++++++++-
 13 files changed, 38 insertions(+), 7 deletions(-)
 delete mode 100644 tests/source/cfg_match/format_me_please.rs
 create mode 100644 tests/source/cfg_match/format_me_please_1.rs
 create mode 100644 tests/source/cfg_match/format_me_please_2.rs
 create mode 100644 tests/source/cfg_match/format_me_please_3.rs
 create mode 100644 tests/source/cfg_match/format_me_please_4.rs
 delete mode 100644 tests/target/cfg_match/format_me_please.rs
 create mode 100644 tests/target/cfg_match/format_me_please_1.rs
 create mode 100644 tests/target/cfg_match/format_me_please_2.rs
 create mode 100644 tests/target/cfg_match/format_me_please_3.rs
 create mode 100644 tests/target/cfg_match/format_me_please_4.rs

diff --git a/src/test/mod.rs b/src/test/mod.rs
index fa88e3e03c1..36e6aa84fc2 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -501,9 +501,9 @@ fn format_files_find_new_files_via_cfg_match() {
             }
         }
         assert_eq!(
-            3,
+            6,
             write_result.len(),
-            "Should have uncovered an extra file (format_me_please.rs) via lib.rs"
+            "Should have uncovered an extra file (format_me_please_x.rs) via lib.rs"
         );
         assert!(handle_result(write_result, None).is_ok());
     });
diff --git a/tests/source/cfg_match/format_me_please.rs b/tests/source/cfg_match/format_me_please.rs
deleted file mode 100644
index 7de75301649..00000000000
--- a/tests/source/cfg_match/format_me_please.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-
-pub fn     hello(   )  {     }
diff --git a/tests/source/cfg_match/format_me_please_1.rs b/tests/source/cfg_match/format_me_please_1.rs
new file mode 100644
index 00000000000..8cc758d76d6
--- /dev/null
+++ b/tests/source/cfg_match/format_me_please_1.rs
@@ -0,0 +1,2 @@
+
+pub fn     format_me_please_1(   )  {     }
diff --git a/tests/source/cfg_match/format_me_please_2.rs b/tests/source/cfg_match/format_me_please_2.rs
new file mode 100644
index 00000000000..e394cc50458
--- /dev/null
+++ b/tests/source/cfg_match/format_me_please_2.rs
@@ -0,0 +1,2 @@
+
+pub fn     format_me_please_2(   )  {     }
diff --git a/tests/source/cfg_match/format_me_please_3.rs b/tests/source/cfg_match/format_me_please_3.rs
new file mode 100644
index 00000000000..1f5d9104024
--- /dev/null
+++ b/tests/source/cfg_match/format_me_please_3.rs
@@ -0,0 +1,2 @@
+
+pub fn     format_me_please_3(   )  {     }
diff --git a/tests/source/cfg_match/format_me_please_4.rs b/tests/source/cfg_match/format_me_please_4.rs
new file mode 100644
index 00000000000..c33b3d3e691
--- /dev/null
+++ b/tests/source/cfg_match/format_me_please_4.rs
@@ -0,0 +1,2 @@
+
+pub fn     format_me_please_4(   )  {     }
diff --git a/tests/source/cfg_match/lib.rs b/tests/source/cfg_match/lib.rs
index ce64d30f1b1..2f0accac7d7 100644
--- a/tests/source/cfg_match/lib.rs
+++ b/tests/source/cfg_match/lib.rs
@@ -1,5 +1,16 @@
+#![feature(cfg_match)]
+
 std::cfg_match! {
+    test => {
+        mod format_me_please_1;
+    }
     target_family = "unix" => {
-        mod format_me_please;
+        mod format_me_please_2;
+    }
+    cfg(target_pointer_width = "32") => {
+        mod format_me_please_3;
+    }
+    _ => {
+        mod format_me_please_4;
     }
 }
diff --git a/tests/target/cfg_match/format_me_please.rs b/tests/target/cfg_match/format_me_please.rs
deleted file mode 100644
index 421e195a2fb..00000000000
--- a/tests/target/cfg_match/format_me_please.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub fn hello() {}
diff --git a/tests/target/cfg_match/format_me_please_1.rs b/tests/target/cfg_match/format_me_please_1.rs
new file mode 100644
index 00000000000..68488003f0b
--- /dev/null
+++ b/tests/target/cfg_match/format_me_please_1.rs
@@ -0,0 +1 @@
+pub fn format_me_please_1() {}
diff --git a/tests/target/cfg_match/format_me_please_2.rs b/tests/target/cfg_match/format_me_please_2.rs
new file mode 100644
index 00000000000..005863ab7a6
--- /dev/null
+++ b/tests/target/cfg_match/format_me_please_2.rs
@@ -0,0 +1 @@
+pub fn format_me_please_2() {}
diff --git a/tests/target/cfg_match/format_me_please_3.rs b/tests/target/cfg_match/format_me_please_3.rs
new file mode 100644
index 00000000000..e6913d3ca18
--- /dev/null
+++ b/tests/target/cfg_match/format_me_please_3.rs
@@ -0,0 +1 @@
+pub fn format_me_please_3() {}
diff --git a/tests/target/cfg_match/format_me_please_4.rs b/tests/target/cfg_match/format_me_please_4.rs
new file mode 100644
index 00000000000..8cb3fd7708b
--- /dev/null
+++ b/tests/target/cfg_match/format_me_please_4.rs
@@ -0,0 +1 @@
+pub fn format_me_please_4() {}
diff --git a/tests/target/cfg_match/lib.rs b/tests/target/cfg_match/lib.rs
index ce64d30f1b1..2f0accac7d7 100644
--- a/tests/target/cfg_match/lib.rs
+++ b/tests/target/cfg_match/lib.rs
@@ -1,5 +1,16 @@
+#![feature(cfg_match)]
+
 std::cfg_match! {
+    test => {
+        mod format_me_please_1;
+    }
     target_family = "unix" => {
-        mod format_me_please;
+        mod format_me_please_2;
+    }
+    cfg(target_pointer_width = "32") => {
+        mod format_me_please_3;
+    }
+    _ => {
+        mod format_me_please_4;
     }
 }