Course contentsShow
← Rust
Lesson 1577 of 1,91013. Metaprogramming with MacrosFree lesson

Static Analysis Integration

You will combine macros with custom clippy lints to enforce architectural rules across large codebases.

In this lesson, you will learn how to bridge the gap between declarative macros and static analysis to enforce architectural constraints that the compiler cannot naturally verify.

The Problem: Architectural Drift

In large-scale systems, macros are often used to define components (e.g., #[service], #[handler]). While macros generate the necessary glue code, they cannot natively prevent a developer from accidentally violating architectural boundaries—like a low-level data layer calling a high-level UI component. By integrating custom Clippy lints, we can treat our macros as "architectural markers" and programmatically forbid forbidden cross-references.

Integrating Macros with Lints

The strategy is to use the macro to attach a specific attribute or marker trait to generated code, which your custom clippy plugin then scans.

  1. Mark the Boundaries: Define a declarative macro that applies a specific attribute, such as #[arch_layer(data)], to your structs.
  2. Lint the AST: Create a custom Clippy pass that implements LateLintPass. In the check_expr or check_item method, look for the arch_layer attribute.
  3. Enforce Rules: If your lint detects a "Data" layer calling a "Service" layer, it emits a rustc diagnostic error, effectively turning an architectural violation into a compile-time failure.
// A simplified view of the integration
declare_clippy_lint! {
    pub ARCHITECTURAL_VIOLATION,
    Deny,
    "prevents unauthorized cross-layer calls"
}

// Inside your custom lint pass:
fn check_expr(cx: &LateContext, expr: &Expr) {
    if is_calling_forbidden_layer(expr) {
        cx.span_lint(ARCHITECTURAL_VIOLATION, expr.span, "Layer violation detected!");
    }
}

Why This Matters

While macros ensure your code is syntactically correct and boilerplate-free, they don't inherently protect the "integrity" of your system design. By coupling them with custom lints, you treat your codebase as a formal graph of components. When a developer uses your macro, they aren't just generating code; they are opting into an architectural contract that the build process will strictly uphold. This transforms soft documentation guidelines into hard, automated constraints.

Key Takeaway: Use declarative macros to label architectural components, then implement custom Clippy lints to scan for and block forbidden communication patterns between those components at compile time.