Expand description
§Linter (lint)
Solidity linter for identifying potential errors, vulnerabilities, gas optimizations, and style guide violations. It helps enforce best practices and improve code quality within Foundry projects.
§Supported Lints
forge-lint includes rules across several categories:
- High Severity:
incorrect-exp: Flags^(bitwise xor) used where**(exponentiation) was likely intended.incorrect-shift: Warns against shift operations where operands might be in the wrong order.unchecked-call: Low-level calls should check the success return value.erc20-unchecked-transfer: ERC20transferandtransferFromcalls should check the return value.arbitrary-send-erc20: FlagstransferFrom/safeTransferFromcalls whosefromargument is not provablymsg.senderoraddress(this).arbitrary-send-erc20-permit: Flags arbitrarytransferFromcalls preceded by a coveringpermit; on non-permit tokens with a fallback (e.g. WETH) the permit silently succeeds and previously-approved tokens can be drained.controlled-delegatecall: Flagsdelegatecallcalls whose target is not provably trusted.encode-packed-collision: Flagsabi.encodePacked()calls with multiple dynamic-type arguments (string,bytes, dynamic arrays) that can produce hash collisions.enumerable-loop-removal: Flagsremoveon an EnumerableSet inside a loop that also iterates a set withat; swap-and-pop removal corrupts the iteration.function-selector-collision: Flags colliding selectors between a proxy and the statically typed implementation API targeted by its fallback.rtlo: Flags Unicode bidirectional override characters (“Trojan Source”, CVE-2021-42574) that can hide malicious code.reentrancy-eth: Flags uncapped ETH-transferring low-level calls followed by writes to state that was read before the call.unprotected-initializer: Upgradeable initializers should not be callable on the implementation contract.
- Medium Severity:
assert-state-change: Flags state-modifying expressions insideassert()arguments.boolean-cst: Flags misuse of boolean constants.dangerous-unary-operator: Flags an assignment whose=is fused to a unary operator (=-,=~), e.g.x =- 1, which parses asx = -1instead of the intended compoundx -= 1.divide-before-multiply: Warns against performing division before multiplication in the same expression, which can cause precision loss.incorrect-erc20-interface: Flags ERC20 interfaces and implementations with non-compliant function signatures.incorrect-erc721-interface: Flags ERC721 interfaces and implementations with non-compliant function signatures.incorrect-strict-equality: Dangerous strict equality check on an externally-influenced value (ETH balance, ERC-20 balance).mapping-deletion:deleteon a value containing a mapping does not clear the mapping.reentrancy-no-eth: Flags non-ETH external calls followed by writes to state that was read before the call.tautological-compare: Comparing an expression with itself is always true or false.tx-origin: Flags use oftx.originin authorization-like predicates.uninitialized-local: Local variable is read before being explicitly initialized.uninitialized-state: State variable is read in functions but never written, so it always returns its zero-value default.unsafe-typecast: Typecasts that can truncate values should be checked.unused-return: Return value of an external call is not used.locked-ether: Contracts that can receive ETH but have no mechanism to send it out.non-reentrant-not-first:nonReentrantshould be the first modifier on guarded entry points.weak-prng: Flags randomness-like expressions derived from predictable on-chain values.
- Low Severity:
block-timestamp: Warns whenblock.timestampis used in a comparison, as it may be manipulated by validators.calls-loop: External calls inside loops can cause denial-of-service if a call reverts or exhausts gas.delegatecall-loop: Payable functions should not usedelegatecallinside a loop.deprecated-oz-function: OpenZeppelin deprecatedSafeERC20.safeApprove(usesafeIncreaseAllowance/safeDecreaseAllowance) andAccessControl._setupRole(use_grantRole).empty-block: Flags regular functions with an empty body; constructors,receive/fallback,virtualfunctions, functions with modifiers and value-lesspayablefunctions are exempt.incorrect-modifier: Modifiers should not be able to finish without executing_or reverting.inconsistent-type-names: Flags shorthanduint/intdeclarations when the same contract also usesuint256/int256.missing-events-access-control: Access control changes should emit events.missing-zero-check: Address parameter is used in a state write or value transfer without a zero-address check.reentrancy-events: Events emitted after external calls can be reordered or fabricated by a reentrant callee and mislead off-chain consumers.return-bomb: External calls with a gas limit should not consume unbounded return data.solmate-safe-transfer-lib: solmate’s releasedSafeTransferLibdoes not check that the token has code, so token operations against a token-less address succeed silently.
- Informational / Style Guide:
boolean-equal: Boolean comparisons to constants should be simplified.too-many-digits: Numeric literals with 5+ consecutive zeros are error-prone.pascal-case-struct: Flags for struct names not adhering toPascalCase.mixed-case-function: Flags for function names not adhering tomixedCase.mixed-case-variable: Flags for mutable variable names not adhering tomixedCase.screaming-snake-case-const: Flags forconstantvariable names not adhering toSCREAMING_SNAKE_CASE.screaming-snake-case-immutable: Flags forimmutablevariable names not adhering toSCREAMING_SNAKE_CASE.unused-import: Unused imports should be removed.unaliased-plain-import: Use named imports{A, B}or aliasimport ".." as X.named-struct-fields: Prefer initializing structs with named fields.unsafe-cheatcode: Usage of unsafe cheatcodes that can perform dangerous operations.multi-contract-file: Prefer having only one contract, interface, or library per file.interface-file-naming: Interface file names should be prefixed withI.interface-naming: Interface names should be prefixed withI.pragma-inconsistent: Flags projects whose source files declare different Solidity pragma version requirements.redundant-base-constructor-call: Flags explicit empty base-constructor arguments (e.g.is A()) when the base requires no arguments.missing-inheritance: Flags contracts that implement every external function of an interface without explicitly inheriting from it.low-level-calls: Direct use of low-level calls should be avoided.event-fields:addressevent parameters should beindexedfor efficient log filtering.todo-comment: Detects unresolvedTODOandFIXMEmarkers in comments.unused-error: Custom error declarations that are never referenced should be removed.literal-instead-of-constant: A literal value repeated inside a contract should be a named constant.function-init-state: State variable initializers run before the constructor; depending on a non-pure function or another state variable there observes partial state.internal-function-used-once: Internal functions referenced exactly once can usually be inlined into their caller.cyclomatic-complexity: functions with a cyclomatic complexity above 11 should be split into smaller functions.incorrect-using-for:using ... fordirectives naming a library with no function applicable to the type attach nothing and should be fixed or removed.modifier-used-only-once: Modifiers invoked by exactly one function can usually be inlined as checks in that function.
- Gas Optimizations:
asm-keccak256: Recommends using inline assembly forkeccak256for potential gas savings.cache-array-length: Recommends caching storage dynamic array lengths used inforloop conditions.costly-loop: Flags storage variable writes inside loops; accumulate into a local variable and write once after the loop instead.could-be-immutable: Recommends declaring constructor-only state variables asimmutable.could-be-constant: Recommends declaring never-written state variables with a compile-time-constant initializer asconstant.custom-errors: Recommends using custom errors instead of strings and plain reverts for potential gas savings.external-function:publicfunctions never called internally should be declaredexternalto avoid copying reference-type arguments into memory.unused-state-variables: State variables that are never used should be removed.var-read-using-this: Reads of state variables (or otherview/purefunctions) viathiscause an unnecessarySTATICCALL; access them directly.write-after-write: Flags storage variables written consecutively without the first value ever being read; only the final write is needed.
- Code Size:
unwrapped-modifier-logic: Recommends wrapping modifier logic to reduce contract code size.
§Configuration
The behavior of the SolidityLinter can be customized with the following options:
| Option | Default | Description |
|---|---|---|
with_severity | None | Filters active lints by their severity (High, Med, Low, Info, Gas, CodeSize). None means all severities. |
with_lints | None | Specifies a list of SolLint instances to include. Overrides severity filter if a lint matches. |
without_lints | None | Specifies a list of SolLint instances to exclude, even if they match other criteria. |
with_description | true | Whether to include the lint’s description in the diagnostic output. |
with_json_emitter | false | If true, diagnostics are output in rustc-compatible JSON format; otherwise, human-readable text. |
§Contributing
Check out the foundry contribution guide.
Guidelines for contributing to forge lint:
§Opening an issue
- Create a short concise title describing an issue.
- Bad Title Examples
Forge lint does not work Forge lint breaks Forge lint unexpected behavior - Good Title Examples
Forge lint does not flag incorrect shift operations
- Bad Title Examples
- Fill in the issue template fields that include foundry version, platform & component info.
- Provide the code snippets showing the current & expected behaviors.
- If it’s a feature request, specify why this feature is needed.
- Besides the default label (
T-Bugfor bugs orT-featurefor features), addC-forgeandCmd-forge-fmtlabels.
§Fixing A Bug
- Specify an issue that is being addressed in the PR description.
- Add a note on the solution in the PR description.
- Add a test case to
lint/testdatathat specifically demonstrates the bug and is fixed by your changes. Ensure all tests pass.
§Developing a New Lint Rule
Check the dev docs for a full implementation guide.
Modules§
Macros§
- declare_
forge_ lint - Macro for defining lints and relevant metadata for the Solidity linter.
- register_
lints - Registers Solidity linter passes that can have both early and late variants.