Skip to content

Lint Categories ​

Every lint in soroban-cost-linter is assigned one of five LintCategory values. This page explains what each category means, which metered Soroban resource it maps to, and how to choose one when adding a new lint.

The categories are defined by the LintCategory enum in soroban_cost_lints/src/lib.rs, and every shipped lint carries its category in the LINT_METADATA registry — the single source of truth. The cargo-cost-lint CLI reads that registry to enumerate lints (--list), to group warnings in the --report output, and to label budget.toml rows under their category. If a documentation page and LINT_METADATA disagree, trust LINT_METADATA.

The five categories ​

CategoryWhat belongs in itSoroban resource dimensionShipped lints
StorageOperationsDirect ledger reads/writes via the Storage, Instance, Persistent, or Temporary accessors.Storage — ledger entry accesses + ledger I/O bytes, the most expensive resourcesoroban_storage_in_loop, loop_invariant_storage_access, soroban_redundant_storage_read, storage_write_without_read, instance_storage_for_unbounded_data
ComputeHost functions that cross the Wasm guest/host boundary and burn CPU budget with each call (ledger, crypto, events, ...).CPU — Wasm instruction execution + host function dispatchunnecessary_host_function_call, host_in_loop, contract_call_in_loop, unbounded_input_loop, signature_verification_in_loop, linear_scan_in_loop, require_auth_in_loop, formatted_panic_payload
MemoryGuest- or host-side allocations that grow with input size, including repeated soroban_sdk::Bytes / Vec / Map mutations.RAM — guest linear memory, hard-capped but not charged in the resource feeredundant_env_clone, soroban_inefficient_bytes_concat, inefficient_bytes_concat, unnecessary_string_to_bytes, bytes_append_in_loop, storage_key_construction_in_loop, map_insert_in_loop, vec_where_slice_could_be_used, string_concat_in_loop
EntryLifecycleLifecycle of contract entries: authorisation, deployment, removal.Storage — ledger space rent / TTL paid when entries are created, extended, or removedextend_ttl_in_loop, persistent_read_without_ttl_extension
SymbolOperationsConstruction and reuse of soroban_sdk::Symbol values.CPU — runtime symbol construction crosses the Wasm–host boundarysymbol_new_for_short_literal

The "what belongs in it" descriptions above are taken verbatim from the LintCategory rustdoc, and the shipped-lint examples are the current LINT_METADATA assignments. The resource dimensions come from the Cost Rationale page, which explains Soroban's full metering model — budgets, fees, and what dominates a contract's resource usage.

How the categories map to the resource model ​

Soroban meters execution along several dimensions, and each category tracks the dominant dimension the lints in it are trying to save:

  • StorageOperations → Storage. Every read or write on an instance, persistent, or temporary storage entry consumes a ledger entry access plus ledger I/O bytes. Storage is the single most expensive resource a typical contract can consume, so repeated, loop-invariant, or redundant storage access is the highest-priority class of lint.
  • Compute → CPU instructions. Host function calls pay a dispatch overhead plus the work the host performs; signature verification, cross-contract calls, and other cryptographic or host-boundary work are among the most expensive calls available. Repeated, invariant, or unbounded CPU work belongs here.
  • Memory → RAM. Needless clones, copies, and growth of SDK containers (Bytes / Vec / Map) allocate and copy guest linear memory. Memory is hard-capped per transaction but is not charged in the resource fee, so these lints are secondary to storage and CPU.
  • EntryLifecycle → Storage (ledger space rent / TTL). Authorising, deploying, extending the TTL of, or removing ledger entries has a lifecycle cost — the rent payments that keep entries alive are priced from ledger size. Lints about when entries are created, extended, or allowed to expire belong here.
  • SymbolOperations → CPU. Symbol::new constructs the symbol at runtime, which is metered; the symbol_short! macro produces a compile-time constant instead. Anything about how Symbol values are constructed or reused belongs here.

Choosing a category for a new lint ​

Ask what the pattern wastes, in resource terms:

  1. Does the pattern touch Soroban storage? A read, write, or existence check on Storage / Instance / Persistent / Temporary — directly, repeatedly, or in a loop — is StorageOperations.
  2. Is the waste CPU work? A metered host call (Ledger, Crypto, Prng, Events, Deployer, ...), signature verification, a cross-contract call, or any computation repeated needlessly is Compute.
  3. Is the waste allocation or copying? A needless .clone(), repeated concatenation, or growing an SDK container inside a loop is Memory.
  4. Does the pattern affect the lifecycle of ledger entries? Authorisation, deployment, TTL extension, or removal — as opposed to the read/write itself — is EntryLifecycle.
  5. Does the pattern build or reuse Symbol values? Runtime symbol construction that could be a compile-time constant is SymbolOperations.

When the answer is not obvious, look at the closest existing lint in LINT_METADATA and follow its category. The category is what the CLI uses to route diagnostics and budget.toml rows, so pick the dominant cost dimension of the pattern rather than a secondary one.

INFO

Before writing a new lint, read Scope: Clippy vs. soroban-cost-linter to confirm the pattern belongs here, then follow the custom lint guide — which points to DEVELOPING_LINTS.md at the repository root, the single authoritative lint-authoring guide.

Built for the Stellar & Soroban ecosystem.