Netherermind is releasing aztec-lint v0.1.0, a deterministic linter for Noir projects with Aztec-specific static analysis.
aztec-lint helps catch correctness, privacy, protocol, soundness, and maintainability issues before code lands in CI or production.
What’s in v0.1.0
- CLI for checking, fixing, and explaining lints
- Canonical lint metadata catalog (single source of truth for lint IDs, levels, docs, lifecycle)
- Runtime rule engine for Noir core + Aztec lint rules
- Deterministic output in text, json, and sarif
- Config loading, suppression handling, safe fix application, and CI-focused behavior
Installation
curl -fsSL ``https://raw.githubusercontent.com/NethermindEth/aztec-lint/main/scripts/install.sh`` | bash
From source
Prerequisite: Rust toolchain compatible with workspace (edition = 2024, rust-version = 1.93.0).
git clone ``git@github.com``:NethermindEth/aztec-lint.git
cd aztec-lint
cargo build --workspace
cargo install --path crates/aztec-lint-cli --bin aztec-lint
Base linting commands
Run default check (equivalent to check with Aztec profile unless overridden):
aztec-lint
Run explicit check:
aztec-lint check
Apply safe fixes:
aztec-lint fix
Preview fixes without writing files:
aztec-lint fix --dry-run
List available rules:
aztec-lint rules
Explain one rule:
aztec-lint explain AZTEC010
Run Aztec profile shortcut:
aztec-lint aztec scan
Common flags
–profile
–changed-only
–format text|json|sarif
–severity-threshold warning|error
–min-confidence high|medium|low
–deny <RULE_ID>
–warn <RULE_ID>
–allow <RULE_ID>
–show-suppressed
Config file discovery:
- aztec-lint.toml (primary)
- noir-lint.toml (fallback)
Unknown rule IDs fail fast before execution (CLI overrides and profile overrides).
2 Likes
eyey! amazing initiative, i’ve run it on our standards repo and found some broken links (instead of src/token_contract/src/test/___.nr it outputs already from inside the token_contract dir: src/test/___.nr which causes cmd+click to be broken in the console) and many false positives, and --fix seems to leave the repo in an un-compilable state, here’s the output an automated report on it:
Summary
This report lists false positives produced by the linter. Each section contains concrete failing instances and suggested changes to linter behavior.
1. NOIR001: “X is declared but never used”
1.1 Failing Instances
The linter reports variables as unused even when they are used.
| File |
Line |
Variable |
Linter says |
Actual usage |
burn_private.nr |
24 |
proxy |
unused |
Used in authwit_cheatcodes::add_private_authwit_from_call(env, owner, proxy, ...) and GenericProxy::at(proxy).forward_private_3(...) |
burn_private.nr |
61 |
proxy |
unused |
Same pattern as above |
burn_private.nr |
81 |
proxy |
unused |
Same pattern as above |
burn_private.nr |
101 |
proxy |
unused |
Same pattern as above |
mint_to_commitment.nr |
7 |
recipient |
unused |
Used in initialize_transfer_commitment(recipient, minter) and check_private_balance(..., recipient, ...) |
mint_to_commitment.nr |
7 |
minter |
unused |
Used in env.call_private(minter, ...) and env.call_public(minter, ...) |
mint_to_commitment.nr |
28 |
recipient |
unused |
Used in commitment initialization and mint call |
mint_to_commitment.nr |
100, 112 |
minter |
unused |
Used in env.call_public(minter, ...) |
mint_to_commitment.nr |
120 |
recipient |
unused |
Used in commitment, check_private_balance, check_total_supply |
mint_to_private.nr |
6 |
minter |
unused |
Used in env.call_private(minter, ...) |
mint_to_public.nr |
6, 50 |
minter |
unused |
Used in mint calls |
tokenized_vault/constructor_with_asset_initial_deposit.nr |
18 |
minter |
unused |
Verify usage; likely used in deployment or constructor |
1.2 Suggested Linter Changes
- Use-def tracking: The linter should correctly track variables used as arguments to function calls (including FFI/external helpers like
add_private_authwit_from_call) and generic/macro invocations (e.g. GenericProxy::at(x)).
- Argument passing: A variable passed as a function argument is used; such bindings must not be flagged as unused.
- Cross-module analysis: Ensure use-def analysis follows usages across module boundaries (e.g.
authwit_cheatcodes, GenericProxy, test utils).
2. NOIR030: “unconstrained value X influences constrained logic”
2.1 Failing Instances
The linter flags values as unconstrained that come from the Aztec test framework and are effectively constrained by the harness.
Affected sources:
TestEnvironment (env)
env.deploy(), env.create_contract_account(), env.create_light_account()
env.view_public(...), env.call_private(...), env.call_public(...)
env.utility_context_at(), env.public_context_at()
- Helper return values:
get_public_balance, get_total_supply, get_private_balance
Approx. count: ~350 diagnostics across all test files.
| Category |
Example variables |
Typical use |
| Test env |
env |
Test harness |
| Addresses |
token_contract_address, vault_address, owner, recipient, asset_address |
From deploy(), create_*_account() |
| View results |
total_supply, total_assets, max_deposit, balance_of_private |
From env.view_public() |
| Commitments |
commitment, validity_commitment |
From env.call_private() or PartialUintNote |
2.2 Suggested Linter Changes
- Test framework context: In
unconstrained fn blocks inside #[test] modules, values coming from TestEnvironment and its methods should be treated as test-only and not flagged as unconstrained inputs to constrained logic.
- Trusted test APIs: Provide a way (e.g.
#[aztec_test] or a config option) to mark Aztec test APIs (TestEnvironment, env.*) as trusted, so their return values are not treated as unconstrained.
- Scoping: Apply NOIR030 differently in test code vs production code, or allow suppressing it for test modules.
3. NOIR002: “X shadows an existing binding in scope”
3.1 Failing Instances
The linter reports shadowing in cases where bindings are intentionally introduced in separate scopes (conditionals and closures).
| File |
Lines |
Variables |
Pattern |
utils.nr |
24–25, 28–29 |
owner, recipient |
let (owner, recipient) = if cond { let owner = ...; let recipient = ...; (owner, recipient) } else { ... } |
utils.nr |
45–47, 50–52 |
owner, recipient, minter |
Same conditional pattern with three variables |
utils.nr |
67–68, 71–72 |
owner, recipient |
Same pattern in another setup function |
utils.nr |
348 |
notes |
Closure: let notes = env.utility_context_at(..., |context| { let notes = set.view_notes(...); notes }) |
utils.nr |
363 |
value |
Closure: let value = env.public_context_at(..., |context| { let value = context.storage_read(...); value }) |
In each case, the inner binding lives in a separate scope (branch or closure); the outer binding is used to capture the closure’s return value.
3.2 Suggested Linter Changes
- Branch scope: Treat
if/else branches as separate scopes; shadowing across branches (each branch defines its own owner, etc.) should not be reported.
- Closure scope: Treat closure bodies as distinct scopes; bindings inside a closure do not shadow bindings outside it when the inner binding is local to the closure and the outer one holds the closure’s return value.
- Configurable policy: Allow suppressing or relaxing NOIR002 for conditional initialization and callback patterns (e.g. per-function or per-module), or document when shadowing is considered acceptable.
4. Summary
| Error Code |
Reported |
False positives |
Suggested fix |
| NOIR001 |
~15 |
All |
Correct use-def for function args, generics, and cross-module calls |
| NOIR030 |
~350 |
All |
Treat Aztec test APIs as trusted in test context |
| NOIR002 |
~16 |
All |
Model branch/closure scope correctly for shadowing |
1 Like
Thanks for reporting this, as the tool is in heavy development and this kind of feedback is very valuable .
Could you tell me which version are you running? I’ve just released v0.6.0 which ships additional Aztec lints, also many false positives were corrected in v0.3.0 so if you are not using latest release please update and try linting again.
Meanwhile I will check against your repo locally and apply any patches required.
2 Likes
@wei3erHase i’ve created a patch release v0.6.1.
This fixes all the false positives in aztect-standards.
Please update and run again.
eyey! thanks for the quick reply! confirming 0.6.1 fixes false positives on token_contract, we still have broken links (the output aims to i.e. src/test/utils.nr:129:22 when the real path – that u can cmd+click to “go to line” – is actually src/nft_contract/src/test/utils.nr:129:22)
1 Like
Just released v0.6.2 which should solve the path issues
2 Likes