Back

Static Analysis Rules for Delphi, Free Pascal & VB6

The complete LegacyLint rule catalog — 192 checks for Delphi, Object Pascal and VB6, covering bugs, security, performance, and maintainability. Each rule links to its rationale and a remediation example.
Rule Category Severity Description
.pas

LL003

CodeSmell
Warning
Empty implementations often hide unfinished logic
.pas

LL099

CodeSmell
Info
A constructor that only calls inherited adds no value and can be removed
.pas

LL012

Complexity
Warning
Deeply nested branches increase cognitive load and defect rates
.pas

LL089

Complexity
Warning
Methods exceeding 10 decision points are hard to test and maintain reliably
.pas

LL098

Complexity
Warning
Three levels of nested loops create O(n^3) complexity and are difficult to reason about
.pas

LL005

Delphi
Warning
Deprecated units can block upgrades and increase maintenance cost
.pas

LL006

Delphi
Warning
`with` obscures binding and can introduce subtle bugs
.pas

LL017

Delphi
Warning
`AnsiString` often introduces encoding bugs in modern Unicode codebases
.pas

LL020

Delphi
Warning
`Variant` weakens type safety and can hide conversion/runtime errors
.pas

LL027

Delphi
Warning
ShortString is a legacy fixed-length type that limits interoperability with modern Unicode APIs
.pas

LL028

Delphi
Warning
Real48 is a legacy 6-byte floating-point type with limited precision and no hardware support
.pas

LL029

Delphi
Warning
Raw memory management is error-prone and bypasses Delphi's type safety
.pas

LL030

Delphi
Warning
Untyped pointers bypass compile-time checks and are a frequent source of memory errors
.pas

LL031

Delphi
Warning
Re-entering the message loop causes reentrancy bugs and unpredictable UI state
.pas

LL032

Delphi
Warning
Sleep blocks the calling thread, freezing the UI or wasting thread-pool resources
.pas

LL033

Delphi
Info
The Tag property is untyped and fragile, leading to hard-to-trace bugs when used for logic
.pas

LL034

Delphi
Error
Halt and RunError terminate the process immediately without cleanup, risking data loss
.pas

LL035

Delphi
Warning
Mutating list items in a loop without BeginUpdate causes excessive UI repaints and flicker
.pas

LL037

Delphi
Warning
Storing a component reference without FreeNotification risks dangling pointers when the referenced component is destroyed
.pas

LL038

Delphi
Info
Version conditionals targeting very old Delphi versions add noise without practical benefit
.pas

LL039

Delphi
Warning
Untyped var/const parameters bypass compile-time type checking and can cause subtle corruption
.pas

LL040

Delphi
Warning
Inline assembler is non-portable across CPU architectures and hard to maintain
.pas

LL062

Delphi
Info
Using LowerCase/UpperCase for comparison allocates a temporary string unnecessarily
.pas

LL063

Delphi
Warning
Legacy PChar functions are error-prone and replaced by modern string operations
.pas

LL074

Delphi
Info
Creating UI controls in constructors bypasses the form designer and complicates layout
.pas

LL075

Delphi
Info
Deprecated components like TWebBrowser and TDDEServerConv lack modern support
.pas

LL009

Design
Warning
Overgrown classes correlate with poor cohesion and regressions
.pas

LL014

Design
Warning
Shared mutable globals create hidden coupling and race risks
.pas

LL036

Design
Warning
Large event handlers couple business logic to the UI, hindering testability and reuse
.pas

LL066

Design
Warning
Classes with more than 30 methods likely violate the single responsibility principle
.pas

LL067

Design
Info
Deep hierarchies via TCustom-prefixed ancestors increase coupling and fragility
.pas

LL097

Design
Info
A method heavily accessing another object's members likely belongs on that object
.pas

LL011

Encapsulation
Warning
Public mutable fields bypass invariants and validation
.pas

LL001

Maintainability
Warning
Long methods increase defect risk and reduce reviewability
.pas

LL002

Maintainability
Info
Signals technical debt, but not always immediate risk
.pas

LL004

Maintainability
Warning
Very large units are harder to understand and change safely
.pas

LL007

Maintainability
Warning
High-arity signatures are difficult to use correctly and evolve
.pas

LL010

Maintainability
Warning
Unstructured jumps hinder readability and control-flow reasoning
.pas

LL013

Maintainability
Warning
Unnamed literals obscure intent and make policy changes harder
.pas

LL051

Maintainability
Info
Commented-out code clutters the source and is better tracked by version control
.pas

LL052

Maintainability
Warning
Methods with many local variables are hard to follow and often need decomposition
.pas

LL053

Maintainability
Warning
Boolean expressions with many operators are hard to read and error-prone to modify
.pas

LL054

Maintainability
Info
Repeated literals make changes error-prone and increase the risk of inconsistency
.pas

LL058

Maintainability
Warning
A uses clause with many units indicates high coupling and makes the unit fragile to changes
.pas

LL059

Maintainability
Warning
Unreachable statements waste reader attention and may indicate a logic error
.pas

LL060

Maintainability
Info
Many return points make control flow harder to reason about and test
.pas

LL065

Maintainability
Info
Nested procedures reduce readability and cannot be unit-tested independently
.pas

LL068

Maintainability
Info
Methods where over half the lines are comments often contain stale documentation
.pas

LL069

Maintainability
Warning
Hardcoded Windows paths break portability and complicate deployment
.pas

LL072

Maintainability
Info
Unused parameters add noise and may indicate incomplete implementations
.pas

LL088

Maintainability
Warning
Units with an implementation section but no code are dead weight in the project
.pas

LL090

Maintainability
Warning
Listing the same unit twice adds noise and can mask refactoring mistakes
.pas

LL092

Maintainability
Warning
Files with more than 50 methods are hard to navigate and likely have too many responsibilities
.pas

PRF001

Performance
Warning
Repeated string concatenation in loops causes O(n^2) memory allocations
.pas

PRF002

Performance
Warning
FindComponent and FieldByName perform linear searches; calling them inside loops is O(n*m)
.pas

PRF003

Performance
Info
Repeated property getter calls may execute logic each time; caching avoids redundant work
.pas

PRF004

Performance
Warning
Creating and destroying objects per iteration wastes allocations and increases GC pressure
.pas

PRF005

Performance
Warning
Large fixed-size arrays on the stack risk stack overflow, especially in recursive methods
.pas

PRF006

Performance
Info
Calling Length or Count in every loop iteration re-evaluates the expression unnecessarily
.pas

PRF007

Performance
Info
Format with a single %s substitution adds overhead with no formatting benefit over concatenation
.pas

PRF008

Performance
Info
Many concatenation operators on one line cause repeated memory allocations
.pas

PRF009

Performance
Info
Untyped TList requires manual casting and lacks ownership semantics, increasing error risk
.pas

PRF010

Performance
Warning
Sorting inside a loop multiplies the cost dramatically; sort once after all mutations
.pas

PRF011

Performance
Info
Many try blocks in one method suggest exceptions used as flow control, which is slow
.pas

PRF012

Performance
Info
Assigning value types to Variant inside loops causes repeated heap allocations
.pas

PRF013

Performance
Info
Converting a value to a string and back wastes CPU cycles with no net effect
.pas

PRF014

Performance
Warning
Passing large records or arrays by value copies the entire structure on each call
.pas

PRF015

Performance
Info
Calling the same function three or more times wastes cycles when the result is stable
.pas

PRF016

Performance
Info
Copy(S, I, 1) allocates a temporary string when direct indexing S[I] is faster
.pas

PRF017

Performance
Info
Many log calls in a single method add I/O overhead and clutter log output
.pas

PRF018

Performance
Warning
Creating or executing TRegEx inside a loop recompiles the pattern on each iteration
.pas

PRF019

Performance
Warning
Executing SQL in a loop multiplies database round-trips and degrades throughput
.pas

PRF020

Performance
Warning
Creating TMemoryStream per iteration wastes allocations when one stream can be reused
.pas

LL008

Reliability
Warning
Swallowing exceptions hides failures and complicates recovery
.pas

LL015

Reliability
Warning
Nil assignment after free reduces accidental use-after-free patterns
.pas

LL016

Reliability
Error
Missing `inherited Destroy` can leak resources and corrupt cleanup
.pas

LL018

Reliability
Warning
Parsing failures via exceptions create brittle control flow and runtime faults
.pas

LL019

Reliability
Warning
Object lifetime without guaranteed cleanup frequently leaks resources
.pas

LL024

Reliability
Warning
Calling Exit in a function without setting Result can return undefined values
.pas

LL026

Reliability
Warning
The absolute keyword aliases memory locations and is error-prone in modern Delphi
.pas

LL041

Reliability
Warning
Catching all exceptions without a type makes debugging and recovery much harder
.pas

LL042

Reliability
Warning
Re-raising the caught exception variable resets the stack trace, losing diagnostic information
.pas

LL043

Reliability
Error
Exit inside a finally block silently swallows any pending exception, masking failures
.pas

LL044

Reliability
Info
A case statement without an else branch silently ignores unexpected values
.pas

LL045

Reliability
Error
Freeing the same object twice causes access violations or heap corruption
.pas

LL046

Reliability
Warning
Reading a variable before assignment yields unpredictable values and subtle bugs
.pas

LL047

Reliability
Warning
Integer multiplication or truncating casts can silently overflow, causing wrong results
.pas

LL048

Reliability
Warning
Unsynchronized access to global state from threads causes data races and corruption
.pas

LL049

Reliability
Warning
Objects created without a matching Free leak memory and OS handles
.pas

LL050

Reliability
Warning
Dividing by a variable without a zero guard risks runtime exceptions
.pas

LL061

Reliability
Warning
Omitting inherited Create skips ancestor initialization and can corrupt object state
.pas

LL070

Reliability
Warning
Identical branches are almost always copy-paste errors that mask intended logic
.pas

LL071

Reliability
Warning
Assigning a variable to itself is a no-op and indicates a copy-paste bug
.pas

LL073

Reliability
Info
Catching generic Exception hides specific failures and makes debugging harder
.pas

LL076

Reliability
Warning
Indexing a string without a Length guard risks an index-out-of-range exception at runtime
.pas

LL077

Reliability
Warning
A case branch with no statements silently ignores matching values and may hide bugs
.pas

LL078

Reliability
Warning
Chaining member access on nil-returning functions causes access violations
.pas

LL079

Reliability
Warning
While True or repeat/until False without Break/Exit will hang the application
.pas

LL080

Reliability
Error
:= inside an if or while condition is almost certainly a typo for = comparison
.pas

LL081

Reliability
Info
Inc/Dec on small integer types can silently overflow without range checking enabled
.pas

LL082

Reliability
Warning
Calling
.pas

LL083

Reliability
Warning
Comparing signed and unsigned integers can produce unexpected results with negative values
.pas

LL084

Reliability
Warning
Entering a critical section without a matching Leave risks deadlocks
.pas

LL085

Reliability
Warning
Objects created before try/except without cleanup in the except block leak on exception
.pas

SEC001

Security
Error
Embedded secrets are high-impact and immediately exploitable
.pas

SEC002

Security
Warning
Concatenation is a common SQL injection precursor
.pas

SEC003

Security
Warning
Risky file APIs can bypass safe path handling and validation
.pas

SEC004

Security
Warning
Weak randomness is unsafe for tokens, keys, or security decisions
.pas

SEC005

Security
Error
Process execution APIs become high-risk when arguments can be influenced by untrusted input
.pas

SEC006

Security
Warning
HTTP, FTP, and Telnet transmit data in cleartext, risking interception
.pas

SEC007

Security
Warning
Passing user-controlled variables as format strings to Format() can cause crashes or information disclosure
.pas

SEC008

Security
Error
User-controllable file paths can escape intended directories and access arbitrary files
.pas

SEC009

Security
Warning
MD5 and SHA1 are vulnerable to collision attacks and should not be used for security
.pas

SEC010

Security
Info
Hardcoded IPs reduce flexibility and can expose internal network topology
.pas

SEC011

Security
Warning
LoadLibrary with a variable path enables DLL hijacking if the search order is exploited
.pas

SEC012

Security
Info
Direct registry access ties code to Windows and can expose sensitive configuration data
.pas

SEC013

Security
Error
Disabling SSL validation exposes connections to man-in-the-middle attacks
.pas

SEC014

Security
Warning
Direct typecasts skip runtime type checking and can cause access violations or data corruption
.pas

SEC015

Security
Error
Storing passwords without hashing is a critical vulnerability if the data store is compromised
.pas

SEC016

Security
Warning
Building XML by concatenation allows injection of malicious elements
.pas

SEC017

Security
Warning
Concatenating user input into LDAP filters enables directory injection attacks
.pas

SEC018

Security
Info
Unsanitized user data in logs can forge entries or inject control characters
.pas

SEC019

Security
Warning
Predictable temp file names allow attackers to pre-create symlinks (TOCTOU attack)
.pas

SEC020

Security
Error
Move/CopyMemory without SizeOf can overwrite adjacent memory and enable exploits
.pas

SEC021

Security
Warning
Deserializing untrusted streams can instantiate arbitrary classes and execute code
.pas

SEC022

Security
Error
Hardcoded connection strings expose server addresses and credentials in source
.pas

SEC023

Security
Warning
DES, RC4, and Blowfish are cryptographically broken and offer inadequate protection
.pas

SEC024

Security
Warning
Handlers that process requests without auth checks expose functionality to unauthenticated users
.pas

SEC025

Security
Warning
Sending raw exception messages to clients leaks internal paths, types, and stack traces
.bas

VB008

Security
Warning
Launching external commands is command injection the moment any argument is user-influenced
.bas

VB009

Security
Error
A secret in source ships to every developer machine and every backup; in VB6, = is both assignment and comparison, and either way the literal is the leak
.bas

VB010

Security
Warning
Concatenated SQL plus user input is injection; ADO with assembled WHERE clauses is the most common security hole in legacy VB6
.bas

VB014

Security
Warning
Raw-memory and process APIs (CopyMemory, WriteProcessMemory, GetProcAddress) crash on a wrong length and bypass every safety the language has
.bas

VB015

Security
Warning
Http/ftp/telnet URLs in VB6 source feed WinInet or browser controls in plaintext; localhost is exempt
.bas

VB035

Security
Warning
Kill deletes files, accepts wildcards, and asks nothing; with any user-influenced path it is arbitrary file deletion
.pas

LL021

Style
Info
Consistent naming improves readability and follows Delphi conventions
.pas

LL022

Style
Info
Interfaces should follow the I prefix convention for discoverability
.pas

LL023

Style
Info
Comparing to True/False is redundant and reduces readability
.pas

LL025

Style
Info
Assigned() is the idiomatic Delphi way to check for nil and works with both objects and interfaces
.pas

LL055

Style
Info
Extremely long single-line declarations reduce readability and complicate diffs
.pas

LL056

Style
Info
Consistent UPPER_CASE naming for constants improves scan-ability and convention compliance
.pas

LL057

Style
Info
A shared prefix on enum values clarifies membership and prevents name collisions
.pas

LL064

Style
Info
Comparing with '' is less intention-revealing than Length or IsEmpty checks
.pas

LL086

Style
Info
Names over 40 characters impair readability and complicate code navigation
.pas

LL087

Style
Info
Mixing camelCase and PascalCase in one class confuses contributors
.pas

LL091

Style
Info
Unscoped unit names like SysUtils are ambiguous and deprecated in modern Delphi
.pas

LL093

Style
Info
More than 2 consecutive blank lines waste screen space and reduce code density
.pas

LL094

Style
Info
Trailing spaces cause noisy diffs and serve no purpose
.pas

LL095

Style
Info
Mixing tabs and spaces for indentation causes inconsistent rendering across editors
.pas

LL096

Style
Info
Lines over 120 characters cause horizontal scrolling and are harder to review
.pas

LL100

Style
Info
Type-encoding prefixes are redundant in a strongly typed language and reduce readability
.bas

VB001

VB6
Warning
Without Option Explicit a misspelled variable silently becomes a new Variant instead of a compile error
.bas

VB002

VB6
Warning
It swallows every error after it — the VB6 equivalent of an empty except block
.bas

VB003

VB6
Warning
Unstructured jumps hide control flow; On Error GoTo is exempt because it is VB6's structured error handling
.bas

VB004

VB6
Warning
Variants defer type errors to runtime; Dim a, b As Long types only b and makes a a Variant without any warning
.bas

VB005

VB6
Warning
CreateObject and As Object resolve members at runtime, so a typo becomes error 438 in production instead of a compile error
.bas

VB006

VB6
Warning
DoEvents re-enters the message loop mid-procedure — the same re-entrancy trap as Application
.bas

VB007

VB6
Warning
Obsolete construct removed after VB6; one of the first things a migration has to untangle
.bas

VB011

VB6
Warning
An untyped Function or Property Get returns Variant, spreading the typing trap to every call site
.bas

VB012

VB6
Warning
An untyped parameter is a Variant and ByRef by default, so the callee can silently reassign the caller's variable
.bas

VB013

VB6
Error
A bare End terminates the process without unload events or cleanup — and in a compiled executable a leftover Stop does the same
.bas

VB016

VB6
Warning
+ concatenates strings until an operand is a numeric Variant or Null — then it silently adds, or yields Null
.bas

VB017

VB6
Warning
Procedures past ~60 lines accumulate the local state and branching that make legacy VB6 unreviewable; same threshold as the Delphi rule so mixed codebases are judged alike
.bas

VB018

VB6
Warning
An empty body is either dead scaffolding or a handler that silently swallows the event it was wired to
.bas

VB019

VB6
Info
Work markers are debt the compiler cannot see; surfacing them keeps the backlog honest
.bas

VB020

VB6
Warning
A module past ~800 code lines has become the place where everything lands; splitting it is the first step of any untangling
.bas

VB021

VB6
Warning
Unexplained numeric literals; the Const that fixes the finding is also the documentation the number never had
.bas

VB022

VB6
Warning
Nesting past four levels hides which condition guards which statement; inverting guards flattens it
.bas

VB023

VB6
Info
A Select Case with no Case Else does nothing for unexpected values — and the unexpected value is usually the bug report
.bas

VB024

VB6
Warning
More than five parameters is usually a missing Type or class — and with VB6's default ByRef, every one is a variable the callee might rewrite
.bas

VB025

VB6
Info
While
.bas

VB026

VB6
Warning
IIf is a function, not a conditional — both branches run every time, so a guard inside it guards nothing
.bas

VB027

VB6
Error
Without an Exit Sub above the handler label, the successful path runs straight into the error handling — one of the most common real bugs in VB6
.bas

VB028

VB6
Warning
Public/Global variables in a
.bas

VB029

VB6
Info
An unhandled error walks up the call chain and terminates the program on the user's screen; long procedures doing real work deserve a handler
.bas

VB030

VB6
Warning
DefInt A-Z changes what undeclared names mean by first letter, invisibly and per module
.bas

VB031

VB6
Info
Arrays in this module start at 1 while every other module starts at 0 — an off-by-one waiting at each module boundary
.bas

VB032

VB6
Info
UCase(a) = UCase(b) allocates two strings per comparison and mishandles locale-specific casing
.bas

VB033

VB6
Warning
CallByName resolves a member from a string at runtime — no compile check, no rename safety, and user-influenced strings choose which method runs
.bas

VB034

VB6
Warning
SendKeys types into whatever window has focus at that instant; it breaks when a dialog steals focus and trips UAC/UIPI on modern Windows
.bas

VB036

VB6
Warning
As String * N pads with spaces and truncates silently, so equality checks fail in ways that depend on the data
.bas

VB037

VB6
Info
Dim s$ is the pre-As spelling of types; explicit but only readable by those who know the six-symbol table
.bas

VB038

VB6
Warning
Dim x As New re-creates the object the moment it is touched after Set x = Nothing, so lifetime checks lie and teardown code resurrects what it released
.bas

VB039

VB6
Warning
A loop on rs
.bas

VB040

VB6
Warning
ReDim Preserve copies the whole array every call; growing one element at a time inside a loop is quadratic
.bas

VB041

VB6
Warning
A locally opened ADO/DAO recordset or connection that is never closed holds locks and pool connections until garbage collection
.bas

VB042

VB6
Warning
A transaction never committed or rolled back in its procedure keeps its locks until the connection dies
.bas

VB043

VB6
Warning
Open
.bas

VB044

VB6
Warning
An Open without a Close keeps the handle and its lock for the life of the process; the error path is where it usually escapes
.bas

VB045

VB6
Info
Close without a file number closes every open file in the process, including ones another procedure was still writing
.bas

VB046

VB6
Warning
App
.bas

VB047

VB6
Warning
Removing from a collection while For Each walks it skips elements or raises error 5 mid-loop, whichever the data decides
Object Pascal — Delphi & Free Pascal Visual Basic 6
Reconnecting… Connection lost. Reload