You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Detectors — Sonar rule catalogue for the Static Code Analysis Tool for Delphi
Canonical list of all supported and planned analysis rules, ordered by
severity (Blocker → Critical → Major → Minor → Info). The catalogue
follows the Sonar 50-rule taxonomy and adds a handful of bonus detectors
specific to this tool.
Remaining 4 open slots all need type-inference / flow-analysis / cross-unit symbol resolution: #20 ResultNotChecked, #22 CyclicUnitDep, #42 UnnecessaryCast, #49 DeprecatedAPI. #16 UninitVar has a conservative MVP (SCA166) — full path-sensitivity remains open for Phase 3.
The 21 Pascal-AST detectors below follow the Sonar 50-rule taxonomy.
The 23 DFM detectors in the dedicated section are form-file
specific and do not appear in the Sonar catalogue — they operate on
the DFM lexer + parser + component graph (and FormBinder for
Pascal-AST coupling) introduced with v0.10.0. The SonarDelphi-
migration cluster (SCA120-131) below covers Delphi-specific
correctness checks that SonarDelphi ships and we ported. The bulk
of the SCA060-119 naming / formatting / structural checks is not
enumerated here yet — see rules/sca-rules.json
for the canonical roster.
Object created with .Create but no Free/FreeAndNil/Destroy anywhere in the method body
✅
uLeakDetector2
2
SCA002
EmptyExcept — empty except block
except block without an executable statement — exceptions are silently swallowed
✅
uCodeSmells2
3
SCA008
NilDeref — nil pointer without check
Object field or parameter dereferenced without a prior Assigned() check
✅
uNilDeref
4
SCA003
SQLInjection — string concatenation in SQL
SQL command built by + concatenation with user input — no parameterised query
✅
uSQLInjection
5
SCA004
HardcodedSecret — password/token in code
Literal assigned to a variable whose name contains password, token, secret, key
✅
uHardcodedSecret
🟠 Critical (10)
#
SCA
Rule
Description
Status
Unit
6
SCA010
DivByZero — possible division by zero
Integer division or modulo where the divisor can be zero (no upfront check)
✅
uDivByZero
7
SCA134
UseAfterFree — object used after Free
Variable used after Free/FreeAndNil without being re-assigned
✅
uUseAfterFree
8
SCA009
MissingFinally — resource without try/finally
Object created, method has try/except but no try/finally for cleanup
✅
uMissingFinally
9
SCA005
FormatMismatch — wrong arg count in Format()
The number of %s/%d placeholders in the format string does not match the argument list
✅
uFormatMismatch
10
SCA135
AbstractNotImpl — abstract method not implemented
Concrete class inherits from an abstract base but doesn't implement all abstract methods
✅
uAbstractNotImpl (within-unit only)
11
SCA132
ExceptionTooGeneral — exception type too broad
except on E: Exception instead of a specific type — masks unexpected errors
✅
uExceptionTooGeneral
12
SCA136
LeakInConstructor — exception in constructor without cleanup
Constructor can raise after partial initialisation without calling Free
✅
uLeakInConstructor
13
—
MissingDestructor — destructor missing / field not freed
Class with object fields: no destructor, or a field isn't freed in Destroy
✅
uFieldLeak
14
SCA137
IntegerOverflow — arithmetic overflow
Multiplication or exponentiation on Integer/Word without a prior range check
✅
uIntegerOverflow (Int64 target only)
15
—
RaiseWithoutClass — bare raise
A bare raise outside an except block — produces an Access Violation
✅
uRaiseOutsideExcept
🟡 Major — Reliability (10)
#
SCA
Rule
Description
Status
Unit
16
SCA166
UninitVar — uninitialised variable
Local variable read before being assigned on every code path
🟡
MVP shipped as SCA166 (uUninitVar.pas) — conservative single-method scope without full path-sensitivity. Slot #16 stays 🟡 partial until Phase 3 (CFG + symbol table). See Konzept_SCA166_UninitVar.md.
17
SCA011
DeadCode — unreachable code
Statements after Exit, Break, Continue or raise at the same nesting level
✅
uDeadCode
18
SCA150
BoolAlwaysTrue — boolean always true/false
Comparison such as x >= 0 for Cardinal or Length(s) >= 0 — always evaluates to True
✅
uBoolAlwaysTrue (Length-pattern only)
19
SCA144
FloatEquality — floating-point comparison with =
if a = b where a or b is Single/Double/Extended
✅
uFloatEquality
20
—
ResultNotChecked — return value ignored
A function call whose result (e.g. an error code) is discarded
🔲
21
SCA149
MissingOverride — override missing
Method overrides a parent's virtual/dynamic method without override
✅
uMissingOverride (within-unit only)
22
—
CyclicUnitDep — cyclic unit dependency
Unit A uses unit B (interface), unit B uses unit A (interface)
🔲
23
SCA145
ExceptInDestructor — exception from destructor
Destructor contains code that may raise without a try/except
✅
uExceptInDestructor
24
—
PublicFieldNoProperty — public field instead of property
public field exposed directly instead of via property with getter/setter
✅
uPublicField
25
SCA139
FreeWithoutNil — Free without nil-out
obj.Free not followed by obj := nil or FreeAndNil — dangling pointer possible
✅
uFreeWithoutNil
🟡 Major — Maintainability (10)
#
SCA
Rule
Description
Status
Unit
26
SCA012
LongMethod — method too long
Method body exceeds 50 executable lines
✅
uLongMethod
27
—
TooManyParams — too many parameters
Method has more than 5 parameters
✅
uLongParamList
28
SCA022
CyclomaticComplexity — McCabe complexity > 10
Number of branching paths (if, case arm, for, while, repeat, on handler, and/or/xor) exceeds 10
✅
uCyclomaticComplexity
29
SCA018
DeepNesting — nesting depth > 4
Code block indented more than four levels deep
✅
uDeepNesting
30
SCA021
DuplicateBlock — duplicated code block
Identical block (≥ DuplicateBlockMinLines, default 8 normalized lines) appears more than once in the same file
✅
uDuplicateBlock (SCA021) — line-based sliding window, normalises trim/lowercase/whitespace-collapse, skips boilerplate (begin/end/else/try/finally/except, pure comments) and if/end branching blocks
31
SCA138
GodClass — god class
Class has more than 20 methods or more than 15 instance fields
✅
uGodClass
32
SCA014
MagicNumber — magic number without constant
Numeric literal (other than 0 and 1) used directly in code instead of a named constant
✅
uMagicNumbers
33
SCA146
BooleanParam — boolean as flag parameter
Method takes a Boolean parameter used internally for branching
✅
uBooleanParam
34
SCA140
MultipleExit — more than 3 exit points
Method contains more than three Exit calls
✅
uMultipleExit
35
SCA141
LargeClass — class too big
Single-class unit exceeds 500 lines of implementation
✅
uLargeClass
🔵 Minor — Code Smells (10)
#
SCA
Rule
Description
Status
Unit
36
—
UnusedVar — unused local variable
Variable declared in the var block but never read (or only written)
✅
uUnusedLocal
37
—
UnusedMethod — unused private method
Private method never called inside the unit
✅
uUnusedPrivateMethod
38
—
UnusedUnit — unit in uses not used
Unit listed in uses whose symbols are never referenced
✅
uUnusedUses
39
—
CommentedCode — commented-out code
Block of commented Pascal code (// or { }) without explanation
✅
uCommentedOutCode
40
SCA019
TodoComment — TODO/FIXME without ticket
Comment contains TODO, FIXME, HACK, XXX without an issue reference
✅
uTodoComment
41
SCA020
EmptyMethod — empty method
Method only contains inherited, or is completely empty
✅
uEmptyMethod
42
—
UnnecessaryCast — redundant type cast
Cast to the same type or to a direct ancestor without extension
🔲
43
SCA151
ConstantReturn — method always returns the same value
Every path returns the same literal — should be a constant
✅
uConstantReturn
44
—
LongLine — line too long
Line exceeds 120 characters (configurable via [Detectors] MaxLineLength)
✅
uTooLongLine
45
—
MixedIndent — mixed indentation (tabs + spaces)
Line contains both tab and space indentation
✅
uTabulationCharacter
⚪ Info (5)
#
SCA
Rule
Description
Status
Unit
46
SCA152
HardcodedString — literal instead of resourcestring
User-visible string as a literal instead of a resourcestring declaration
📐 DFM detectors — form-file specific (not in 50-rule Sonar catalogue)
These run against .dfm files using a dedicated DFM lexer + parser
component graph, with TFormBinder coupling the form to its
companion .pas AST and TDfmRepoIndex providing repo-wide
cross-form lookups. All ship with before/after fix hints in the
help panel and DUnitX tests.
Component declared in the DFM is never referenced — not in the form's own code, not by another unit via the global form variable (Form1.Comp, resolved through TSymbolReferenceIndex), and not by another component inside the DFM (DataSource=, Action=, …). Likely dead after refactoring. Ships at fcLow (below the default fcMedium confidence filter — opt-in via --min-confidence low); emits nothing without the repo-wide symbol index. Persistent TFields, embedded frames, and FindComponent-by-name units are deliberately skipped in v1. Known v1 gap: cross-unit mutations where the component is a middle chain token (Form.Comp.Prop := x / .Method) are not yet recognised.
Twelve checks ported from the SonarDelphi rule set. They cover
Delphi-specific correctness gaps that are not represented in the
generic Sonar 50-rule taxonomy: exception/raise hygiene, function
result discipline, type-cast traps around Free / Char / Unicode, and
locale-dependent format calls. All ship with before/after fix hints
in the help panel and a DUnitX test fixture.
ID
Rule
Description
Severity
Type
Unit
SCA120
MissingRaise
EFoo.Create('msg'); allocates an exception object without raise — the error path is silently skipped
Error
Bug
uMissingRaise
SCA121
RoutineResultUnassigned
Function body finishes without writing Result (or <FunctionName> := ...) — return value is undefined
Error
Bug
uRoutineResultAssigned
SCA122
ReRaiseException
on E: T do ... raise E; discards the original stack trace — use bare raise; to keep it
Warning
Bug
uReRaiseException
SCA123
CastAndFree
TFoo(x).Free — the type-cast has no effect on which Destroy runs (Destroy is virtual)
Hint
Code Smell
uCastAndFree
SCA124
InstanceInvokedConstructor
obj.Create — invokes constructor as method on an existing instance, skips allocation and re-runs field initialisation over live data
Error
Bug
uInstanceInvokedConstructor
SCA125
InheritedMethodEmpty
Override whose entire body is inherited; — serves no purpose, remove it
Hint
Code Smell
uInheritedMethodEmpty
SCA126
NilComparison
Use Assigned(x) / not Assigned(x) instead of x = nil / x <> nil — Pascal convention
Hint
Code Smell
uNilComparison
SCA127
RaisingRawException
raise Exception.Create('...') — base class carries no semantic information, callers cannot filter selectively
Warning
Code Smell
uRaisingRawException
SCA128
DateFormatSettings
StrToDate(s), FormatFloat(...) etc. without TFormatSettings depend on the system locale — breaks across machines / users
Warning
Bug
uDateFormatSettings
SCA129
UnicodeToAnsiCast
AnsiString(s) / UTF8String(s) / RawByteString(s) silently drops characters outside the active code page
Warning
Bug
uUnicodeToAnsiCast
SCA130
CharToCharPointerCast
PChar('A') is not PChar("A") — the cast treats the 16-bit codepoint as a raw memory address
Error
Bug
uCharToCharPointerCast
SCA131
IfThenShortCircuit
Math.IfThen / StrUtils.IfThen evaluate both branches — no short-circuit semantics, use if/then/else instead
Nine detectors added after auditing the mORMot2
sources, targeting patterns that recur across large low-level Delphi codebases:
threading primitives, raw heap allocation, dynamic-array growth, byte-level
buffer manipulation, PChar arithmetic, multi-target with blocks, typed
exception handlers, string casts from raw pointers, Win64 pointer arithmetic.
All ship with before/after fix hints and a DUnitX test fixture.
ID
Rule
Description
Severity
Type
Unit
SCA153
UnpairedLock
<id>.Lock / EnterCriticalSection / TMonitor.Enter followed by a matching unlock in the same routine without an enclosing try/finally — exception path leaks the lock and deadlocks the next caller
Warning
Bug
uUnpairedLock
SCA154
MoveSizeOfPointer
Move / FillChar / CopyMemory / ZeroMemory called with SizeOf(PXxx) where PXxx is a pointer type — copies only 4/8 bytes (the pointer size), not the intended buffer
Warning
Bug
uMoveSizeOfPointer
SCA155
WithMultipleTargets
with A, B do (two or more comma-separated receivers) — ambiguous member lookup; adding a method to either A or B silently changes the body's meaning
Hint
Code Smell
uWithMultipleTargets
SCA156
GetMemWithoutFreeMem
GetMem / AllocMem / ReallocMem followed by a matching FreeMem in the same routine without an enclosing try/finally — exception path leaks the raw heap buffer
Warning
Bug
uGetMemWithoutFreeMem
SCA157
SetLengthAppendInLoop
SetLength(arr, Length(arr) + N) inside a for/while/repeat loop — quadratic realloc cost; grow once before the loop or use block-grow
Warning
Code Smell
uSetLengthAppendInLoop
SCA158
PointerArithmeticOnString
PChar(s) +/- offset (or PAnsiChar / PWideChar) without a prior empty-check on s — PChar('') is NIL, arithmetic on nil triggers Access Violation
Warning
Bug
uPointerArithmeticOnString
SCA159
EmptyOnHandler
on E: SomeException do ; (or empty begin end) — typed exception handler silently swallows a specific exception class; worse than bare except end because the typed annotation looks intentional
Warning
Bug
uEmptyOnHandler
SCA160
StringFromPointer
string(P) / AnsiString(P) / UTF8String(P) cast from a P-prefixed pointer assumes a null-terminator — reads past the buffer end if the terminator is missing; heap overread
Warning
Bug
uStringFromPointer
SCA161
PointerSubtraction
Cardinal(P1) - Cardinal(P2) (or Integer / LongWord / LongInt variants) truncates the upper 32 bits of a 64-bit pointer on Win64; use PtrUInt / NativeInt