Linting

The DSL linter is a compiler pass that analyzes symbolic operators during .compile(). It is designed to catch likely mistakes early, explain what they mean, and give practical next steps.

Linting runs through nkdsl.compiler.passes.diagnostics.SymbolicDiagnosticsPass.

Why linting exists

Symbolic operators are expressive, which also means they can fail in subtle ways that are easy to miss in code review:

  • free parameters left unresolved

  • static index expressions outside Hilbert bounds

  • divisions by runtime state values that may be zero

  • terms that never emit due to constant-false predicates

  • generated states that violate support, constraints, or local basis rules

Linting standardizes these findings into stable NKDSL-* codes so teams can search, automate, and gate builds consistently.

When linting runs

In the default pipeline, linting executes before normalization:

  1. nkdsl.compiler.passes.validation.SymbolicValidationPass

  2. nkdsl.compiler.passes.diagnostics.SymbolicDiagnosticsPass

  3. nkdsl.compiler.passes.normalization.SymbolicNormalizationPass

Post-cache passes (analysis and fusion) run afterward on cache misses.

Configuration

Lint behavior is controlled by nkdsl.SymbolicCompilerOptions:

  • diagnostics_enabled: enable/disable linting

  • diagnostics_min_severity: minimum reported/enforced level (info, warning, error)

  • fail_on_warnings: treat warnings as compile-blocking

  • max_diagnostics: cap number of reported diagnostics

  • lint_state_sample_size: sampled source-state count for connectivity checks

  • lint_branch_sample_cap: cap sampled branch evaluations

  • lint_max_exact_hilbert_states: limit exact support-membership checks to manageable Hilbert sizes

Example:

from nkdsl import SymbolicCompilerOptions

opts = SymbolicCompilerOptions(
    diagnostics_enabled=True,
    diagnostics_min_severity="warning",
    fail_on_warnings=True,
    lint_state_sample_size=64,
    lint_branch_sample_cap=4096,
)

Reading lint output

Diagnostics are printed in a structured, readable block:

Diagnostics summary: total=2 (errors=1, warnings=1, info=0)
Read more: https://nkdsl.readthedocs.io/en/latest/dsl/linting/messages.html
- [NKDSL-E001] ERROR @ my_op.term0
  Unresolved free symbol(s): %J.
  Suggestion: Replace with constants or bind symbols before compilation. DSL compilation requires all free symbols to be resolved.
  Docs: https://nkdsl.readthedocs.io/en/latest/dsl/linting/messages.html#lint-code-nkdsl-e001

Every diagnostic includes:

  • a stable code (for example NKDSL-W302)

  • severity

  • location (operator and term when available)

  • message and suggested fix

  • a direct documentation link for that code

Severity semantics

Lint severities are intentionally policy-oriented:

  • error means compilation should usually stop until resolved

  • warning means likely correctness/runtime risk that many teams gate in CI

  • info means non-blocking quality signal (readability, diagnostics coverage, or analysis completeness)

For symbol diagnostics specifically, remember that only unresolved symbols trigger NKDSL-E001. Symbols declared with default=... are considered resolved.

You can enforce stricter behavior with diagnostics_min_severity and fail_on_warnings.

Lint message catalog

Use Lint Messages for the complete per-code reference, including:

  • what each lint means

  • typical causes

  • concrete examples

  • practical remediation guidance

Important notes

  • Connectivity diagnostics are sample-based and intended as early warnings.

  • No-warning output is strong signal, but not a formal proof of correctness.

  • For strict CI workflows, combine:

    • diagnostics_min_severity="warning"

    • fail_on_warnings=True

    to fail compilation on warning-level issues.