Skip to main content

wowlab_tidy/languages/toml/rules/cargo/
proc_macro_crate_helper.rs

1#[cfg(test)]
2use googletest::prelude::*;
3
4use super::{
5    assignment_line, cargo_document, dependency_tables, is_cargo_manifest, resolved_dependency_name,
6};
7use crate::{Example, TomlCtx, Violation, violation};
8
9const HELPER_CRATE: &str = "proc-macro-crate";
10
11#[rustfmt::skip]
12const EXAMPLES: &[Example] = &[
13    Example { label: "ordinary proc macro deps", code: "[dependencies]\nproc-macro2 = \"1\"\nquote = \"1\"\nsyn = \"2\"\n", pass: true },
14    Example { label: "no dependencies", code: "[package]\nname = \"foo\"\n", pass: true },
15    Example { label: "direct dependency", code: "[dependencies]\nproc-macro-crate = \"3\"\n", pass: false },
16    Example { label: "renamed dependency", code: "[dependencies]\npmc = { package = \"proc-macro-crate\", version = \"3\" }\n", pass: false },
17    Example { label: "dev dependency", code: "[dev-dependencies]\nproc-macro-crate = \"3\"\n", pass: false },
18];
19
20crate::toml_rule!(
21    toml_cargo_proc_macro_crate_helper,
22    "Ban the proc-macro-crate helper dependency in Cargo manifests.",
23    "Macros should assume they are used through their main crate and emit main-crate paths; supporting renamed or indirect imports is not worth the complexity (M-MACRO-MAIN-CRATE).",
24    Low,
25);
26
27fn check_toml_cargo_proc_macro_crate_helper(ctx: &TomlCtx<'_>) -> Vec<Violation> {
28    if !is_cargo_manifest(ctx.file.rel) {
29        return Vec::new();
30    }
31
32    let Some(document) = cargo_document(ctx) else {
33        return Vec::new();
34    };
35
36    let dependencies = dependency_tables(&document)
37        .into_iter()
38        .flat_map(|table| table.iter())
39        .filter(|(key, value)| resolved_dependency_name(key, value) == HELPER_CRATE);
40
41    dependencies
42        .map(|(key, _)| {
43            violation(
44                ctx.file.rel,
45                assignment_line(ctx.file.lines, key),
46                "depends on proc-macro-crate; macros must emit main-crate paths instead of supporting renamed imports",
47            )
48        })
49        .collect()
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    fn run(source: &str) -> Vec<Violation> {
57        crate::test_support::check_source_toml_at(
58            "crates/foo/Cargo.toml",
59            source,
60            check_toml_cargo_proc_macro_crate_helper,
61        )
62    }
63
64    crate::example_tests!(EXAMPLES, check_toml_cargo_proc_macro_crate_helper);
65
66    #[gtest]
67    fn flags_target_specific_dependency() -> Result<()> {
68        let violations = run("[target.'cfg(windows)'.dependencies]\nproc-macro-crate = \"3\"\n");
69
70        verify_eq!(violations.len(), 1)?;
71
72        Ok(())
73    }
74}