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.
- Mark the Boundaries: Define a declarative macro that applies a specific attribute, such as
#[arch_layer(data)], to your structs. - Lint the AST: Create a custom Clippy pass that implements
LateLintPass. In thecheck_exprorcheck_itemmethod, look for thearch_layerattribute. - Enforce Rules: If your lint detects a "Data" layer calling a "Service" layer, it emits a
rustcdiagnostic 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.