agentleFS
danieletetidanieleteti/delphi-ai-skills/skills/delphi-code-smells/SKILL.md

delphi-code-smells

Skill23 starsChanged 6 days agoApache-2.0View on GitHub

Use when reviewing existing Delphi / Object Pascal code for defects, hunting a memory leak or an access violation, or hardening new code against the classic mistakes. Covers compiler warnings and hints that indicate a real bug, warnings-as-errors, leak detection and how to make a leak fail a build, third-party static analysis, a catalogue of smells each with a search that finds it, and a staged review procedure with evidence labels. Triggers on "code smell", "code review", "review this unit", "refactor this unit", "memory leak", "leak", "leaking", "double free", "access violation", "AV", "static analysis", "lint", "compiler warning", "W1035", "$WARN", "warnings as errors", "FastMM", "ReportMemoryLeaksOnShutdown", "audit this code", "false positive", "review report".

2 other projects use a version of this, 2 of them edited.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 1 is distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Every warning code, directive and API below was produced by dcc32.exe 37.0 on this machine or copied from the shipped RTL/DUnitX source, …

    line 11
  2. Very specific:

    A pattern hit is a candidate, not a finding: Never report one you did not read, and never report one without an evidence label (VERIFIED / …

    line 32
  3. Very specific:

    Read from …\Studio\37.0\source.

    line 55
  4. Very specific:

    Run it with ReportMemoryLeaksOnShutdown := True: If there is a test suite, run that.

    line 90
  5. Very specific:

    Every finding carries file:line, a severity, an evidence label, what goes wrong at runtime, the fix, and how it was verified.

    line 160
  6. Very specific:

    If a compiler warning already covers a finding, cite the code (W1035) — it turns your opinion into something the user can put in their …

    line 163
  7. Distinctive:

    Never report a smell you cannot say how to detect.

    line 167

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Delphi code smells — make the machine find them first

A review pass that starts with an opinion is worth little. A review pass that starts with the compiler and a leak report is worth a lot, because it is objective and free. Do that half first, every time.

Every warning code, directive and API below was produced by dcc32.exe 37.0 on this machine or copied from the shipped RTL/DUnitX source, not recalled. Where something could not be verified it says so.


Critical rules

Facts that, if you get them wrong, make a review confidently wrong.

  • ReportMemoryLeaksOnShutdown only prints. It never fails a build, it says nothing if the process crashes, and it does nothing in a DLL or package that shares another module's memory manager. A silent DLL is not a clean DLL.
  • The leak report names a class and a count, not a line. Globals created in initialization and never freed appear in every report. Run a baseline first and report only the difference.
  • DUnitX's leak monitor is a no-op until you register a real one. The default returns 0 from every measurement, so out of the box every test passes its leak check.
  • There is no blanket "warnings as errors". Promote one at a time, by name: -W^NO_RETVAL on the command line, {$WARN NO_RETVAL ERROR} in source. The W1035 number is not accepted there (F1030 / E1030).
  • The compiler is silent on a discarded function result, a hidden non-virtual method, and an object reference mixed with an interface reference to the same instance. Those are found by search and by reading.
  • FreeAndNil nils your reference, not anybody else's. It does not make a shared object safe to destroy.
  • A pattern hit is a candidate, not a finding. Never report one you did not read, and never report one without an evidence label (VERIFIED / INFERRED / ASSUMED). reference/review-procedure.md.
  • In rg, -L is --follow, multi-line patterns need -U, look-ahead needs -P. Get these wrong and the search silently finds nothing. reference/discovery.md.

What this skill owns, and what it does not

QuestionWhere it is answered
Why does this leak? How does ownership work? Free, try/finally, interfaces, refcounting, TObjectList<T>, TComponent, managed recordsdelphi, reference/memory.md — not restated here
What is the right try/except shape? What must I never catch?delphi, reference/exceptions.md
Synchronize vs Queue, locks, FreeOnTerminatedelphi, reference/concurrency.md
Who frees the object a DMVC action returns? What is ToFree?dmvcframework SKILL.md — not restated here
How do I find the leak? How do I prove it? How do I stop it coming back?here

This skill is diagnosis. When a smell needs semantics to explain, it links; it does not re-teach.


Which unit declares what

Read from …\Studio\37.0\source. A missing uses is the commonest compile error in a probe.

UnitDeclares
System (implicit, never in uses)ReportMemoryLeaksOnShutdown, RegisterExpectedMemoryLeak, UnregisterExpectedMemoryLeak, GetMemoryManagerState, TMemoryManagerState, TSmallBlockTypeState, TInterfacedObject, IInterface, TMethod, TMonitor, WeakAttribute ([weak]), UnsafeAttribute ([unsafe])
System.SysUtilsFreeAndNil, TStringBuilder, Exception, TryStrToInt, TryEncodeDate, Supports, EAbstractError, EAssertionFailed, ERangeError, EIntOverflow, EInvalidPointer, EAccessViolation
System.ClassesTStringList, TStrings, TMemoryStream, TFileStream, TNotifyEvent, TComponent (FreeNotification, RemoveFreeNotification), TThread (Queue, Synchronize, RemoveQueuedEvents, CreateAnonymousThread, FreeOnTerminate)
System.Generics.CollectionsTObjectList<T>, TDictionary<K,V>, TObjectDictionary<K,V> (TDictionaryOwnerships: doOwnsKeys, doOwnsValues)
System.ThreadingTTask, ITask
System.IOUtilsTFile
Vcl.ControlsTControl, including the class property TControl.RaiseOnNonMainThreadUsage
DUnitX.TestFrameworkIMemoryLeakMonitor, IMemoryLeakMonitor2, TTestResultType (member MemoryLeak), IRunResults, EXIT_ERRORS, IgnoreMemoryLeaks (an alias of the one in DUnitX.Attributes)
DUnitX.ServiceLocatorTDUnitXServiceLocator (13 Florence; 12 Athens has TDUnitXIoC in DUnitX.IoC)
System.Sharemem, System.SimpleShareMemShare another module's memory manager. In such a module ReportMemoryLeaksOnShutdown has no effect (RTL comment on the declaration)

Notes that are not obvious from the names:

  • There is no FastMM unit to add. The memory manager is in getmem.inc, which System.pas includes. The full FastMM is a separate download (reference/leaks.md §5).
  • RegisterExpectedMemoryLeak and GetMemoryManagerState are declared platform: using them prints W1002 on 37.0 (seen when compiling a probe).
  • DUnitX.MemoryLeakMonitor.FastMM4 uses no external FastMM unit and registers only when USE_FASTMM4_LEAK_MONITOR is defined. DUnitX.MemoryLeakMonitor.FastMM5 needs FastMM5.pas first in the .dpr and registers itself unconditionally in initialization.
  • FreeAndNil is declared procedure FreeAndNil(const [ref] Obj: TObject) in both the 23.0 and 37.0 trees.

Order of attack — an unfamiliar unit

The five stages (discover, judge, refute, verify, report) are in reference/review-procedure.md. Within discovery, work down this list. Stop reporting when you run out of items that cost the user something.

  1. Compile it with warnings and hints on and read every line of output. Free, objective, and the single highest-yield step. reference/static-analysis.md.
  2. Run it with ReportMemoryLeaksOnShutdown := True. If there is a test suite, run that. reference/leaks.md.
  3. Ownership by hand — every Create in the unit: who frees it, on which paths, including the raising one. This is where the compiler cannot help you.
  4. Exception handling — every except without an on, every empty handler, every try/except that should have been try/finally.
  5. Concurrency — anything reachable from a TThread.Execute or a TTask.Run body.
  6. Everything else. Naming, with, long methods, magic numbers.

A leak the compiler cannot see outranks a naming convention, always. If the review output is a wall of style nits and no lifetime finding, the review was not done. Report defects in the order above, and say for each one what it costs at runtime — a leak, an AV, a silently wrong result. "This is ugly" is not a finding.


The five-minute pass

# 1. what the compiler already knows (see reference/static-analysis.md for the codes)
dcc32.exe -H MyProject.dpr

# 2. one leak run (reference/leaks.md), then the searches in reference/discovery.md
rg -n -U -P --glob '*.{pas,dpr,dpk}' 'PATTERN'

reference/discovery.md has one tested pattern per smell below, for rg and for PowerShell, with what each one misses. Start with patterns 1, 3, 10, 12 and 16 (the numbering of smells.md): they were silent on the correct test code. Patterns 2, 5, 8, 9 and 11 also hit correct code and always need the refutation pass.


Reference files

FileRead it when
reference/review-procedure.mdYou are about to review code and report. The five stages, the refutation checklist, how to verify with a leak probe, the evidence labels (VERIFIED / INFERRED / ASSUMED), the severity words, the report template and when not to report
reference/discovery.mdStage 1. One search per smell (rg and PowerShell, same pattern), with what it found on the test units, whether it also hit the correct code, and what it cannot see
reference/static-analysis.mdCompiler warnings and hints (verified codes), $WARN, turning a warning into an error, the build switches, and an honest note on Pascal Analyzer / SonarDelphi / FixInsight / madExcept / Error Insight
reference/leaks.mdFinding, reproducing and fencing a leak: the shipped memory manager, ReportMemoryLeaksOnShutdown, reading the report, RegisterExpectedMemoryLeak, and DUnitX per-test leak detection — the item that makes a leak fail a build
reference/smells.mdThe catalogue. Each entry: the bad code, what it costs at runtime, the fix, and how to detect it

The defects worth finding, ranked

Detail and detection for each in reference/smells.md; the search for each in reference/discovery.md (same numbers as the entries in smells.md, not as this table).

#DefectCosts you
1Constructor inside the tryDestroy called on uninitialised memory — AV
2Object created and never freed on some pathLeak
3Create in a loop, Free after itLeak, once per iteration
4Object held as both a class reference and an interfaceDouble free, or a dangling pointer
5Two objects holding interfaces to each otherLeak the shutdown report shows as a matched pair
6except endThe diagnosis is deleted; the program carries on wrong
7try/except where try/finally was meantResource is released only when something raised
8Result not assigned on every pathCaller reads garbage. W1035
9Event handler / observer never unsubscribedAV when the publisher fires into a freed subscriber
10A function that returns an object nobody knows they ownLeak, or a double free
11Worker thread touching the VCLAV, once a week, on the customer's machine
12A thread outliving the object it capturedAV at shutdown
13Method silently hiding an ancestor's virtualWrong method runs. W1010
14withA field added later hijacks a name; silent, compiles
15Magic literal used as a status codeWrong branch after any refactor

Reporting a review

  • Every finding carries file:line, a severity, an evidence label, what goes wrong at runtime, the fix, and how it was verified. Not an essay. The template is in reference/review-procedure.md.
  • Lead with anything that leaks, dangles or returns a wrong value. Style last, or not at all.
  • If a compiler warning already covers a finding, cite the code (W1035) — it turns your opinion into something the user can put in their build and never hear from you again about.
  • Say what you searched for and what you dropped after trying to refute it. "None found" is only meaningful next to the list of what was looked for.
  • Never report a smell you cannot say how to detect. If you cannot name a warning, a tool or a search for it, it is a preference, and the user did not ask for your preferences.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.