Missing Lint Detectors in forge lint
this checklist compares Slither and Aderyn detectors against forge lint and merges equivalent detectors into single items.
NOTE: some items are blocked until Solar outputs a CFG (control-flow graph), as they need data-flow analysis, taint tracking, or path-sensitive reasoning.
Implementable Today (only require AST or HIR)
High
Medium
Low
Info / Optimization
Require CFG (blocked on Solar)
these detectors need control-flow graph, data-flow/taint analysis, or path-sensitive reasoning that cannot be done with AST pattern matching alone.
High
Medium
Low
Info / Optimization
Additional context
ref: #11034 (comment)
Missing Lint Detectors in
forge lintthis checklist compares Slither and Aderyn detectors against
forge lintand merges equivalent detectors into single items.Implementable Today (only require AST or HIR)
High
encode-packed-collision—abi.encodePackedwith multiple dynamic types causes hash collisionsmsg-value-loop—msg.valuereused inside a loop (double-spend)delegatecall-loop— Payabledelegatecallinside a looprtlo— Right-to-left-override Unicode character hiding malicious codeincorrect-exp—^(xor) used instead of**(exponentiation)yul-return—returnin assembly/Yul halts entire EVM execution, not just the functionsuicidal— Unprotectedselfdestruct(no access-control modifier)weak-prng—block.timestamp/blockhashused as randomness sourceshadowing-state— Child contract re-declares parent's state variablecontrolled-array-length— Direct assignment to storage array.lengtharray-by-reference— Storage array modified via memory copy has no effectfunction-selector-collision— Colliding 4-byte function selectorsname-reused— Contract name reused across inheritance treeenumerable-loop-removal— Removing items fromEnumerableSetinside a loop usingat+removecorrupts orderMedium
tx-origin—tx.originused for authorizationmapping-deletion—deleteon struct containing a mapping doesn't clear ittautological-compare—x >= xalways truetautology—uint >= 0always true,uint8 < 512always trueerc20-interface— ERC20 functions with wrong return typeserc721-interface— ERC721 functions with wrong return typesboolean-cst—if(false)orb || true— misused boolean constantdomain-separator-collision— Function selector collides with EIP-2612DOMAIN_SEPARATOR()reused-constructor— Base constructor called with args from two inheritance pathsecrecover— Bareecrecoverwithout signature malleability checknon-reentrant-not-first—nonReentrantmodifier not listed firstunsafe-oz-erc721-mint—_mintinstead of_safeMintfor ERC721out-of-order-retryable— Multiple L1→L2 retryable tickets depend on execution orderblock-timestamp-deadline—block.timestampused as swap/tx deadline (proposer can hold)dangerous-unary-operator—x=+1instead ofx+=1Low
calls-loop— External calls inside unbounded loop (DoS risk)require-revert-in-loop—require/revertinside a loop (single failure reverts all)timestamp— Dangerousblock.timestampdependencyshadowing-local— Local variable shadows state variableshadowing-builtin— Variable shadows Solidity builtin (now,assert, etc.)incorrect-modifier— Modifier doesn't always execute_or revertvoid-cst— Calling empty parent constructorcentralization-risk— Single-owner critical functionsdeprecated-oz-function— Deprecated OpenZeppelin functionsolmate-safe-transfer-lib— Solmate SafeTransferLib doesn't check contract existenceempty-block— Empty function bodyempty-require-revert—require()/revert()with no error messagemultiple-placeholders— Modifier with multiple_placeholdersInfo / Optimization
constable-states— State variable never modified; should beconstantimmutable-states— State variable only set in constructor; should beimmutablecache-array-length— Storage.lengthre-read every loop iterationvar-read-using-this—this.xadds unnecessarySTATICCALLoverheadexternal-function—publicfunction never called internally; useexternalunused-state— Unused state variableunused-error— Unused custom error definitionboolean-equal—x == trueinstead of justxredundant-statements— Statement with no side effectassert-state-change— State modification insideassert()too-many-digits—10000000000000000000instead of10 etherliteral-instead-of-constant— Magic numbers should be named constantsdeprecated-standards—throw,msg.gas,sha3(),suicide()erc20-indexed— Transfer/Approval events missingindexedkeywordunindexed-event-address— Event has address params but none indexedfunction-init-state— State variable initialized via non-pure function callmissing-inheritance— Contract implements interface functions but doesn't inherit itsolc-version— Outdated or overly complex pragmapragma— Different Solidity versions used across filescyclomatic-complexity— Function complexity exceeds thresholdincorrect-using-for—using L for Twhere L has no functions taking Tunimplemented-functions— Derived contract missing interface implementationstodo— TODO comments left in production codepush-0-opcode— Usepush0opcode, available since solc 0.8.20inconsistent-type-names— Inconsistent type naming across codebaseinternal-function-used-once— Internal function used only once; inline itmodifier-used-only-once— Modifier used only once; inline itRequire CFG (blocked on Solar)
these detectors need control-flow graph, data-flow/taint analysis, or path-sensitive reasoning that cannot be done with AST pattern matching alone.
High
reentrancy-eth— Reentrancy that drains ETH (state change after external call)reentrancy-no-eth— Reentrancy that manipulates state without ETHreentrancy-balance— Reentrancy exploitingaddress(this).balancecheckarbitrary-send-erc20— Taint: attacker-controlledfromintransferFromarbitrary-send-erc20-permit— Taint:transferFrom+permitfront-runningarbitrary-send-eth— Taint: ETH sent to attacker-controlled destinationunprotected-upgrade— No access control oninitialize()across all pathscontrolled-delegatecall— Taint: user-controlled delegatecall targetuninitialized-state— State variable used before initialization on some pathuninitialized-storage— Storage pointer left uninitialized on some pathprotected-vars— Protected variable accessed without required modifierMedium
locked-ether— Contract accepts ETH (payable) but has no withdrawal pathincorrect-equality— Strict equality onaddress(this).balance(manipulable)unused-return— External call return value ignoredwrite-after-write— Variable written twice without intermediate readuninitialized-local— Local variable used before assignment on some pathcostly-loop—SSTOREinside loop bodyLow
missing-zero-check— Parameter flows to state variable write without!= address(0)checkevents-access— State change on access-control parameter without emitting eventreturn-bomb— Callee can returnbomb caller via unlimited returndata copyvariable-scope— Variable used before declaration is dominatedInfo / Optimization
dead-code— Unreachable internal functions (call graph reachability)reentrancy-benign— Benign reentrancy (double-call, no exploit)reentrancy-events— Reentrancy manipulates event emission orderreentrancy-unlimited-gas— Reentrancy viatransfer/sendwith gas repricing riskAdditional context
ref: #11034 (comment)