Skip to main content

wowlab_tidy/languages/rust/rules/api/
glob_reexport.rs

1use ra_ap_syntax::{
2    AstNode,
3    ast::{self, HasAttrs, HasVisibility},
4};
5
6use crate::{AstCtx, Example, Violation};
7
8#[rustfmt::skip]
9const EXAMPLES: &[Example] = &[
10    Example {
11        label: "pub glob reexport",
12        code: "pub use internals::*;",
13        pass: false,
14    },
15    Example {
16        label: "pub crate glob reexport",
17        code: "pub(crate) use internals::*;",
18        pass: false,
19    },
20    Example {
21        label: "glob inside group",
22        code: "pub use internals::{helpers::*, Config};",
23        pass: false,
24    },
25    Example {
26        label: "private glob import",
27        code: "fn f() { use internals::*; }",
28        pass: true,
29    },
30    Example {
31        label: "named reexports",
32        code: "pub use internals::{Config, Helper};",
33        pass: true,
34    },
35    Example {
36        label: "windows hal forwarding",
37        code: "#[cfg(windows)]\npub use windows_impl::*;",
38        pass: true,
39    },
40    Example {
41        label: "target os hal forwarding",
42        code: "#[cfg(target_os = \"linux\")]\npub use linux_impl::*;",
43        pass: true,
44    },
45    Example {
46        label: "target arch hal forwarding",
47        code: "#[cfg(target_arch = \"wasm32\")]\npub use wasm_impl::*;",
48        pass: true,
49    },
50    Example {
51        label: "unix hal forwarding",
52        code: "#[cfg(unix)]\npub use unix_impl::*;",
53        pass: true,
54    },
55    Example {
56        label: "glob reexport in test module",
57        code: "#[cfg(test)]\nmod tests {\n    pub use internals::*;\n}",
58        pass: true,
59    },
60];
61
62crate::ast_rule!(
63    glob_reexport,
64    "Flag `pub use foo::*` glob re-exports outside platform-cfg'd HAL forwarding.",
65    "Glob re-exports silently widen the public surface and are unreviewable in diffs; re-export items individually (M-NO-GLOB-REEXPORTS).",
66    Medium,
67);
68
69fn check_glob_reexport(ctx: &AstCtx<'_>) -> Vec<Violation> {
70    let public_uses = ctx
71        .nodes::<ast::Use>()
72        .filter(|item| !ctx.is_in_test(item) && item.visibility().is_some())
73        .filter(|item| !has_platform_cfg(item));
74
75    public_uses
76        .flat_map(|item| {
77            item.syntax()
78                .descendants()
79                .filter_map(ast::UseTree::cast)
80                .filter(|tree| tree.star_token().is_some())
81                .map(|tree| {
82                    ctx.violation(
83                        &tree,
84                        "glob re-export — re-export items individually (M-NO-GLOB-REEXPORTS)",
85                    )
86                })
87                .collect::<Vec<_>>()
88        })
89        .collect()
90}
91
92/// Platform-forwarding cfg attributes sanction glob re-exports (HAL pattern).
93fn has_platform_cfg(item: &ast::Use) -> bool {
94    item.attrs().any(|attr| {
95        if attr.simple_name().as_deref() != Some("cfg") {
96            return false;
97        }
98
99        let text: String = attr
100            .syntax()
101            .text()
102            .to_string()
103            .chars()
104            .filter(|ch| !ch.is_whitespace())
105            .collect();
106
107        text == "#[cfg(windows)]"
108            || text == "#[cfg(unix)]"
109            || text.starts_with("#[cfg(target_os=")
110            || text.starts_with("#[cfg(target_arch=")
111    })
112}
113
114crate::tidy_ast_test!(check_glob_reexport, {
115    crate::example_tests!(EXAMPLES, check_glob_reexport);
116});