Skip to content

Add two build_assert! lints, build_assert_not_inlined and build_assert_can_be_const#13

Open
mqqz wants to merge 12 commits intoRust-for-Linux:trunkfrom
mqqz:build-assert-lints
Open

Add two build_assert! lints, build_assert_not_inlined and build_assert_can_be_const#13
mqqz wants to merge 12 commits intoRust-for-Linux:trunkfrom
mqqz:build-assert-lints

Conversation

@mqqz
Copy link

@mqqz mqqz commented Mar 25, 2026

Apologies for dropping a sizeable PR here with no prior heads-up; there's no expectation to review it quickly. I’m very happy to split it into smaller pieces or reorganize it in whatever way would make it easier to review.

This is my attempt at adding two (very related) lints:

warns when a build_assert! condition depends on non-static values and the surrounding function or propagated call chain is not marked #[inline(always)].

warns when a build_assert! condition is already effectively constant and should use a const assert instead of build_assert!.

The implementation ended up needing a small analysis framework due to the non-local nature of the lints. Requirements can propagate through local helpers, wrappers, function pointers, and other indirect calls.

Implementation

The implementation is built around a shared build_assert! analysis pipeline rather than two unrelated lint passes. The core idea is:

  1. identify build_assert! reliably

    collect explicit #[klint::diagnostic_item] annotations from source, if an item is still missing, fallback to existing out-of-band inference.

  2. recover the asserted condition at the source level

    • walk macro ancestry to confirm an expression came from build_assert!
    • map that back to the source callsite of the macro invocation
    • recover the asserted condition span from the expanded HIR shape
  3. classify what that condition depends on

    either Constant, Runtime or Param(FxHashset<usize>).

  4. summarize that per function

    each function body is analyzed into a FunctionSummary:

    • requirement: whether non-constant values flow into build_assert!
    • return_dependency: how the function’s return value depends on inputs
    • const_only_build_asserts: source spans of constant assertions that should trigger the const lint

    we keep recomputing summaries up to a fixed-point i.e. when no other dependencies can be inferred.

  5. propagate requirements through local call relationships

    I reused/refactored and extended the work done to generate a monomorphised use graph for infallible_allocation into mono_graph.rs needed to analyze calls.

    direct local calls:

    • if a local callee summary exists, map callee parameter dependencies onto caller arguments
    • only propagate dependencies for parameters that actually matter to the callee’s build_assert!

    return dependencies:

    • if a local helper returns a value that depends on parameters, callers retain that precision instead of collapsing to generic runtime

    indirect calls:

    • local function-pointer-like flows are tracked within a body
    • mono-level call graph information is used to resolve indirect and dyn-dispatch cases back to source callsites
  6. emit either lint (or none) accordingly :)

Tests

UI coverage was added for both lints.

tests/ui/build_assert_not_inlined.rs tests cover:

  • direct runtime-dependent assertions
  • parameter + const generic mixtures
  • helper calls
  • caller propagation
  • wrapper macros
  • local rebinding
  • function pointers
  • mixed constant/runtime function-pointer calls
  • dyn-dispatch
  • partially-constant callers
  • runtime-dependent match

tests/ui/build_assert_can_be_const tests cover:

  • direct literal const expressions
  • const generics
  • wrapper macros
  • local const-returning helpers
  • const-only match
  • comment/comma conditions
  • message-form const-only assertions

Docs were also added:

  • doc/build_assert_not_inlined.md
  • doc/build_assert_can_be_const.md
    and README entries in the "Implemented Lints" section.

Closes: #7

mqqz added 12 commits March 23, 2026 21:49
Move the mono-item forward/backward graph construction out of
`infallible_allocation` into a shared `mono_graph` module.

This keeps the graph-building logic in one place and makes it reusable
for later analyses that need monomorphized use edges with span
information.

No intended behavioral change.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
Recognize `build_assert` as a special item, preferring explicit
`#[klint::diagnostic_item]` annotations and keeping a conservative
kernel-specific fallback for older trees.

This extends the existing diagnostic-item discovery pattern so later
lints can identify `build_assert!` semantically instead of relying on
ad-hoc matching.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
Later build_assert lints need to identify the asserted condition
semantically, not by depending on one particular macro expansion
shape.

Add the shared helper that:
- finds `build_assert!` through diagnostic-item-backed macro ancestry
- recovers the first macro argument span from the source call site

This gives later lints a stable source-level handle on the condition
even if the macro body changes.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
The `build_assert` analysis needs to track two things cleanly:
- what values a local expression depends on
- what requirement a function imposes on its callers
including scoped restoration of local bindings.

Keeping this data model separate makes the later analysis code more
manageable and avoids mixing state mechanics with propagation logic.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
Add the local analysis that decides whether a build_assert condition
is effectively constant or still depends on caller-visible values.

This includes:
- expression dependency classification
- local bindings and assignments
- direct-call return dependency mapping
- per-body summary computation

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
Extends local analysis with MIR monomorphized graph to resolve
indirect calls and handle value/requirement flows through helpers,
`fn` pointers, and `dyn` dispatch.

This adds:
- indirect calls resolution using the monomorphized graph
- `dyn`-dispatch `impl` methods matching back to source
- function summary computation up to a fixed point

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
`ClosureDiag` is a generic adapter for building diagnostics inline
from a closure rather than `infallible_allocation`-specific logic.

Move it into `diagnostic` and make it `pub` so later lints can reuse
it instead of defining it locally.

No intended behavioural change.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
UI test coverage includes:
  - `const`-only and `const`-generic cases
  - runtime-dependent parameter flow
  - local helper return-value flow
  - wrapper macros
  - function pointer calls
  - `dyn` dispatch, including same-name trait-method cases

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
Add user-facing documentation for the new `build_assert_not_inlined`
lint in `doc/build_assert_not_inlined.md`.

Including:
  - which kinds of value flow are tracked
  - where propagation stops
  - the distinction from `const`-only `build_assert` uses

Add corresponding entry in the "Implemented Lints" section of README.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
Add a lint for `build_assert!` uses whose condition is already
effectively constant.

When the asserted condition does not depend on runtime values,
the lint suggests using a `const` assert instead of relying on
`build_assert!`.

This reuses the shared build_assert analysis and emits alongside
`build_assert_not_inlined`.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
UI test coverage includes:
- `literals`
- `const` generics
- wrapper macros
- local `const`-only helpers

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
Add user-facing documentation for the new `build_assert_can_be_const`
lint in `doc/build_assert_can_be_const.md`.

Including:
  - const-only and const-generic `build_assert!` uses
  - wrapper-macro and local-helper cases
  - the distinction from runtime-dependent `build_assert!` uses

Add corresponding entry in the "Implemented Lints" section of README,
and cross-reference it from the `build_assert_not_inlined` docs.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Warn on build_assert calls without #[inline(always)]

1 participant