{"id":50448457,"url":"https://github.com/nrodear/staticcodeanalyser","last_synced_at":"2026-05-31T23:02:34.836Z","repository":{"id":292644722,"uuid":"981510282","full_name":"nrodear/StaticCodeAnalyser","owner":"nrodear","description":"Static code analysis for Delphi 12 / RAD Studio. IDE plugin + standalone GUI + CLI (same engine). 150+ detectors (Pascal AST + DFM): leaks, SQL injection, dead handlers, hardcoded secrets, locale traps, Win64 pointer bugs. Sonar push, SARIF for CI/CD, Claude AI hand-off.","archived":false,"fork":false,"pushed_at":"2026-05-28T18:33:12.000Z","size":8655,"stargazers_count":16,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-05-28T20:15:32.344Z","etag":null,"topics":["ast-parser","code-quality","code-smell-detector","delphi","delphi-ide","dfm-linter","dfm-scanner","git-integration","ide-plugin","linter","memory-leak-detection","object-pascal","rad-studio","sarif","sonarqube-style","sql-injection-detection","static-analysis","static-code-analysis","tools-api","vcl"],"latest_commit_sha":null,"homepage":"https://github.com/nrodear/StaticCodeAnalyser#main-features","language":"Pascal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nrodear.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-11T09:22:35.000Z","updated_at":"2026-05-27T20:49:43.000Z","dependencies_parsed_at":"2025-05-11T10:39:38.711Z","dependency_job_id":null,"html_url":"https://github.com/nrodear/StaticCodeAnalyser","commit_stats":null,"previous_names":["nrodear/staticcodeanalyser"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/nrodear/StaticCodeAnalyser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nrodear%2FStaticCodeAnalyser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nrodear%2FStaticCodeAnalyser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nrodear%2FStaticCodeAnalyser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nrodear%2FStaticCodeAnalyser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nrodear","download_url":"https://codeload.github.com/nrodear/StaticCodeAnalyser/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nrodear%2FStaticCodeAnalyser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33752286,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast-parser","code-quality","code-smell-detector","delphi","delphi-ide","dfm-linter","dfm-scanner","git-integration","ide-plugin","linter","memory-leak-detection","object-pascal","rad-studio","sarif","sonarqube-style","sql-injection-detection","static-analysis","static-code-analysis","tools-api","vcl"],"created_at":"2026-05-31T23:02:33.961Z","updated_at":"2026-05-31T23:02:34.830Z","avatar_url":"https://github.com/nrodear.png","language":"Pascal","funding_links":["https://img.shields.io/badge/%E2%98%95_Buy_me_a_coffee-paypal.me%2Fnrodear-0070BA?style=for-the-badge\u0026logo=paypal\u0026logoColor=white","https://paypal.me/nrodear","https://img.shields.io/badge/PayPal-paypal.me%2Fnrodear-0070BA?style=flat\u0026logo=paypal\u0026logoColor=white"],"categories":[],"sub_categories":[],"readme":"# Static Code Analysis Tool for Delphi\r\n\r\n[![Buy me a coffee](https://img.shields.io/badge/%E2%98%95_Buy_me_a_coffee-paypal.me%2Fnrodear-0070BA?style=for-the-badge\u0026logo=paypal\u0026logoColor=white)](https://paypal.me/nrodear)\r\n\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg?style=flat)](LICENSE)\r\n[![PayPal](https://img.shields.io/badge/PayPal-paypal.me%2Fnrodear-0070BA?style=flat\u0026logo=paypal\u0026logoColor=white)](https://paypal.me/nrodear)\r\n\r\n\u003e If this plugin saves you time in your Delphi work, a coffee is appreciated. 🙏\r\n\r\n---\r\n\r\n**Delphi static code analysis tool** and **linter** for **RAD Studio 12 (Athens)** —\r\nships as an **IDE plugin** with a dockable tool window plus a **standalone Windows app**.\r\nAST-based analysis with **~150 detectors total**: ~130 Pascal checks for memory leaks,\r\nSQL injection, code smells, security vulnerabilities and code duplication\r\n(including a **Sonar-Delphi-compatible** subset SCA060+), **plus a\r\ndedicated DFM scanner with 22 checks** built on its own DFM lexer + parser + component\r\ngraph paired with the Pascal AST — dead event handlers, hard-coded DB credentials in\r\nform files, circular master-detail wiring, required dataset fields without UI binding,\r\nSQL built from `TEdit.Text`, cross-form coupling, and more. Sonar-style classification\r\nwith a Quality Score. Repo-wide form index for cross-unit analysis. VCS-diff mode\r\ntreats `.dfm` changes as triggers for the companion `.pas`. HTML report with grouped\r\n`.pas`+`.dfm` filter. IDE plugin opens DFM findings as text directly in the Code\r\nEditor. One click on a finding copies an AI-ready Markdown fix prompt to the\r\nclipboard. Open source, MIT-licensed.\r\n\r\n🇩🇪 [Deutsche Version](README_de.md)\r\n\r\n![SCA in action — analysis, findings, hover-overlays inside the Delphi IDE](docs/sca-demo.gif)\r\n\r\n---\r\n\r\n## What this plugin does\r\n\r\nIn one sentence: **Sonar-style analysis for Delphi projects, with no\r\nSonar setup required, running inside the IDE, with a Claude AI hand-off.**\r\n\r\n| Capability | Details |\r\n|------------|---------|\r\n| 🐛 **Bug detection** | ~130 Pascal detectors run against every `.pas` file (MemoryLeak, NilDeref, DivByZero, FormatMismatch, MissingRaise, RoutineResultUnassigned, CharToCharPointerCast, UnpairedLock, GetMemWithoutFreeMem, PointerArithmeticOnString, …) plus 22 DFM detectors against every `.dfm` (dead event handlers, hard-coded DB credentials, circular master-detail, …) — **~150 total** |\r\n| 🔐 **Security checks** | SQLInjection (score-based), HardcodedSecret, HardcodedPath |\r\n| 🧹 **Code smells** | LongMethod, MagicNumber, EmptyExcept, MissingFinally, DeadCode, DuplicateString/Block |\r\n| ⚡ **Incremental analysis** | \"Branch-Changes\" button: only the files modified in the Git/SVN branch — 200 ms instead of 60 s |\r\n| 🤖 **Claude AI prompt** | Click a finding → a complete Markdown block with code context + before/after is copied to the clipboard |\r\n| 📊 **Sonar-style dashboard** | Stat tiles above the grid: Errors / Warnings / Hints / Bugs / Vulnerabilities / Code Quality score |\r\n| 🎯 **Filter \u0026 sort** | Severity dropdown, type dropdown, live search box, clickable column headers |\r\n| 📤 **Export** | CSV, JSON, self-contained HTML report, Jira wiki markup, plain-text clipboard with before/after |\r\n| 🔇 **Suppression** | `// noinspection MemoryLeak` per line, plus `ignore.txt` for whole files |\r\n| 🌓 **Theme aware** | Follows the active IDE theme automatically (Light / Dark / Mountain Mist / Carbon) |\r\n| 💡 **Before/after help** | Every detector has a paired \"wrong way / right way\" code example in the help panel |\r\n\r\n---\r\n\r\n## Main features\r\n\r\n### 1. Static code analysis (~150 detectors total — ~130 Pascal + 22 DFM, Sonar taxonomy)\r\n\r\n**Pascal AST checks (~130)**: **bugs** (MemoryLeak, NilDeref, DivByZero,\r\nFormatMismatch, ReversedForRange, SelfAssignment, VirtualCallInCtor,\r\nMissingRaise, RoutineResultUnassigned, ReRaiseException,\r\nInstanceInvokedConstructor, CharToCharPointerCast, UnicodeToAnsiCast,\r\nDateFormatSettings, IfThenShortCircuit, …), **vulnerabilities**\r\n(SQLInjection, HardcodedSecret, DisabledTlsVerification),\r\n**security hotspots** (HardcodedPath, HttpInsteadOfHttps), **code smells**\r\n(LongMethod, MagicNumber, DeadCode, EmptyExcept, MissingFinally,\r\nCastAndFree, NilComparison, InheritedMethodEmpty, RaisingRawException,\r\n~60 SonarDelphi-compatible naming/formatting checks SCA060-SCA131, …),\r\nand **code duplication** (DuplicateString, DuplicateBlock).\r\n\r\n**DFM checks (22)** on the dedicated form-file lexer + parser +\r\ncomponent graph, paired with the Pascal AST via the FormBinder: dead\r\nevent handlers, hard-coded DB credentials in form files, circular\r\nmaster-detail wiring, required dataset fields without UI binding, SQL\r\nbuilt from `TEdit.Text`, cross-form coupling, duplicate-control\r\nhot-keys, untranslated Caption strings, and more. Repo-wide form\r\nindex for cross-unit analysis.\r\n\r\nEvery finding ships with a before/after fix in the help panel.\r\n\r\n### 2. Incremental VCS-aware analysis (Git + SVN)\r\n\r\nSkip the full project scan. **One click on `Branch-Changes`** is enough:\r\nthe analyser asks `git diff` (or `svn status`) for the `.pas` files\r\ntouched in your branch and runs the detectors only on those.\r\n**~200 ms instead of 60 s** for a typical feature branch — cheap enough\r\nto use as a pre-commit gate. Configuration lives in `analyser.ini`. Full\r\ndetails in [BRANCH_CHANGES.md](BRANCH_CHANGES.md).\r\n\r\n### 3. AI hand-off (Claude prompt with one click)\r\n\r\nClick a finding row in the grid and the clipboard is filled with a\r\n**ready-made Markdown prompt**: finding metadata, code context (±5\r\nlines, with a marker on the offending line), and the before/after fix.\r\nPaste it into Claude with **Ctrl+V** — the AI now has everything it\r\nneeds to suggest a concrete patch.\r\n\r\n---\r\n\r\n## Use cases by deployment mode\r\n\r\nThe same analyser engine ships in **three forms**, each tuned for a\r\ndifferent workflow. Pick by where you sit in the day:\r\n\r\n| Use case | IDE plugin | Standalone EXE (GUI) | CLI (same EXE) |\r\n|---|:---:|:---:|:---:|\r\n| **Inline review while coding** — see Bug/Vuln markers next to the line you just wrote | ✅ live grid + 3 px editor stripe + hover-overlay | — | — |\r\n| **Quick-Fix the current line** — apply a patch suggestion in place | ✅ `Ctrl+Alt+F` | — | — |\r\n| **Navigate findings with the keyboard** | ✅ `Ctrl+Alt+↑/↓` between findings | grid + arrow keys | — |\r\n| **Suppress a false positive on this line** | ✅ `Ctrl+Alt+S` adds `// noinspection RuleName` | manual | manual |\r\n| **Hand a finding to Claude AI** — Markdown prompt with code context | ✅ row-click → clipboard | ✅ row-click → clipboard | — |\r\n| **Branch-changes only** — analyse files touched since `main` / current SVN diff | ✅ branch-button | ✅ branch-button | ✅ `--branch` or `--diff \u003cref\u003e` |\r\n| **Analyse a project outside Delphi** (RAD not installed / batch machine) | — | ✅ pick a folder, click Start | ✅ `analyser.exe \u003cfolder\u003e` |\r\n| **Run as a pre-commit hook** | — | — | ✅ `--min-severity error --quiet --fail-on error`, exit code reflects severity |\r\n| **Run in CI / GitHub Actions** | — | — | ✅ `--report-sarif sca.sarif`, SARIF upload step |\r\n| **Push findings to SonarQube / SonarCloud** | ✅ `Tools → Sonar Push` | ✅ Export → Sonar | ✅ `--sonar-export sca-findings.json --sonar-host \u003curl\u003e --sonar-token \u003ct\u003e` |\r\n| **Generate an HTML report** for stakeholders / Jira attachments | ✅ Export → HTML | ✅ Export → HTML | — |\r\n| **Generate a Claude review prompt** for the whole batch (Tech-Lead workflow) | ✅ Export → Claude prompt | row-click → clipboard | — |\r\n| **CSV / JSON / Jira export** of findings | ✅ Export menu | ✅ Export menu | ✅ (CSV/JSON via flags) |\r\n| **Nightly full-repo scan** + diff-against-baseline | — | ✅ schedule via Task Scheduler | ✅ schedule via `cron` / `schtasks` |\r\n| **Auto-analyse on file save** (Live-Watch) | ✅ opt-in, see [Live-Watch](#live-watch-ide-plugin-only--%EF%B8%8F-risky) | — | — |\r\n| **Author / edit custom rules** (RegEx via `[CustomRules]` ini) | ✅ Tools → Options | ✅ Settings dialog | edit `analyser.ini` directly |\r\n| **Configure detector thresholds** (`LongMethod_MaxLines`, …) | ✅ Settings | ✅ Settings | edit `analyser.ini` |\r\n| **Switch UI language** (EN / DE) | ✅ Tools → Options | ✅ Settings | n/a (CLI is EN-only) |\r\n| **Pick a rule-set profile** (`ide-fast`, `default`, `strict`) | ✅ profile combo | ✅ profile combo | ✅ `--profile \u003cname\u003e` |\r\n| **Configurable keyboard shortcuts** (cnpack-style: capture key → store in INI) | ✅ Settings → Hotkeys | — | — |\r\n\r\n### Which deployment fits which role\r\n\r\n**Developer at the keyboard** → IDE plugin. Tightest feedback loop:\r\ninline markers, jump-to-line, Quick-Fix-in-place, Ctrl+Alt navigation,\r\nclipboard-to-Claude. The plugin shares the SAME engine + rule catalog\r\n+ FixHints as the other two — what you see is what CI will see.\r\n\r\n**Code-reviewer / Tech-lead without RAD Studio open** → Standalone GUI.\r\nSame grid, same filter, same help-panel as the plugin, but works on a\r\nfolder of `.pas`+`.dfm` without the IDE running. Useful for: review on\r\na different machine, overnight batch on a build server with no Delphi\r\nseat, sharing the EXE with a non-Delphi engineer for one-off audit.\r\n\r\n**CI pipeline / pre-commit hook / scheduled task** → CLI mode of the same\r\nEXE. No GUI, exit code reflects severity via `--fail-on \u003clevel\u003e` (0 =\r\nclean / below threshold, 1 = threshold exceeded). Exports available in\r\nCLI: SARIF (`--report-sarif \u003cfile\u003e`) for GitHub Code-Scanning / Azure\r\nPipelines, Sonar-Generic JSON (`--sonar-export \u003cfile\u003e`) for SonarQube\r\nimports. HTML and Claude-prompt exports are GUI-only. Branch-changes\r\nfilter (`--branch` for git current branch vs `main`, or `--diff \u003cref\u003e`\r\nfor an arbitrary base) lets PR builds analyse only what the diff\r\ntouched.\r\n\r\n**All three modes** read the same `analyser.ini`, the same\r\n`rules/sca-rules.json`, the same suppression markers, and emit the\r\nsame finding kinds. Switching between them is free — a finding\r\nsuppressed in the IDE stays suppressed in CI.\r\n\r\n---\r\n\r\n## Quick start\r\n\r\n1. **Build and install** the plugin: open `StaticCodeAnalyserIDE\\StaticCodeAnalyserIDE.dpk`,\r\n   run **Build**, then **Install** (right-click the package in Project\r\n   Manager → **Install**, or use **Component → Install Packages** from\r\n   the menu and pick the package). Without the install step the plugin\r\n   compiles but never appears in the IDE menu.\r\n2. In Delphi: **View → Static Code Analysis Tool for Delphi** — the\r\n   dockable window shows up.\r\n3. Pick a project path → click **Start analysis**.\r\n\r\nFor incremental scans of branch-changed files only, see\r\n[BRANCH_CHANGES.md](BRANCH_CHANGES.md).\r\n\r\n---\r\n\r\n## Sonar integration\r\n\r\nSCA-Findings can be exported as **external issues** to SonarQube /\r\nSonarCloud (complementary to [SonarDelphi](https://github.com/integrated-application-development/sonar-delphi)\r\n— our findings are mORMot-aware, cover DFM files, and ship sonar-foreign\r\nchecks like `TautologicalBoolExpr`, `ConcatToFormat`, `WithStatement`).\r\n\r\n\u003e **Tested with**: SonarQube Community Build 26.5+ (Sonar 10+, MQR mode).\r\n\u003e SCA findings import as external issues via Generic Issue Format and sit\r\n\u003e alongside Sonar's built-in default profile (**Sonar Way**) — no conflict,\r\n\u003e no override. Works with both SonarQube Server and SonarCloud.\r\n\r\n```powershell\r\n# Configure once (IDE: Tools \u003e Options \u003e Sonar Integration, or CLI):\r\nanalyser.exe --sonar-test `\r\n  --sonar-host http://localhost:9000 `\r\n  --sonar-token squ_xxxxx `\r\n  --sonar-project my-delphi-project\r\n\r\n# Run analysis and emit Generic Issue Format for sonar-scanner\r\nanalyser.exe --path . --full --sonar-export sca-findings.json\r\nsonar-scanner   # picks up via sonar.externalIssuesReportPaths\r\n```\r\n\r\nEach rule carries SonarQube MQR fields (`cleanCodeAttribute` + `impacts`) so\r\nfindings show up correctly in the MQR dashboard. Token-storage in the IDE\r\nuses Windows DPAPI (Current-User-Scope) — no plaintext secrets in `analyser.ini`.\r\n\r\nFull setup: [docs/sonar-setup.md](docs/sonar-setup.md). Config-resolver\r\nreference: [docs/sonar-config.md](docs/sonar-config.md).\r\n\r\n---\r\n\r\n## What is detected (~150 detectors — ~130 Pascal + 22 DFM)\r\n\r\nFindings fall into one of **five Sonar categories**:\r\n\r\n| Category | Detector | Severity |\r\n|----------|----------|----------|\r\n| **Bug** | `MemoryLeak` (LeakDetector + FieldLeak) | Error / Warning |\r\n| | `NilDeref` (nil dereference) | Error |\r\n| | `DivByZero` (division by zero) | Error / Warning |\r\n| | `FormatMismatch` (format vs argument count) | Error |\r\n| **Vulnerability** | `SQLInjection` (score-based) | Error |\r\n| | `HardcodedSecret` (API keys, passwords) | Error |\r\n| **Security Hotspot** | `HardcodedPath` (`C:\\…`, `/etc/…`) | Warning |\r\n| **Code Smell** | `EmptyExcept` (silent swallow) | Warning |\r\n| | `MissingFinally` (Free outside finally) | Warning |\r\n| | `DeadCode` (unreachable after exit/raise) | Warning |\r\n| | `UnusedUses` (optional, default off) | Hint |\r\n| | `LongMethod`, `LongParamList` | Hint |\r\n| | `MagicNumber` (in if conditions) | Hint |\r\n| | `DebugOutput` (`OutputDebugString` etc.) | Warning |\r\n| | `DeepNesting` | Warning |\r\n| | `TodoComment` (TODO/FIXME/HACK) | Hint |\r\n| | `EmptyMethod` | Hint |\r\n| **Code Duplication** | `DuplicateString` (same literal, ≥3 occurrences) | Hint |\r\n| | `DuplicateBlock` (≥ `DuplicateBlockMinLines`, default 8 identical lines) | Hint |\r\n| **Read error** | `FileReadError` (parser hang or oversized file) | Error |\r\n\r\nEvery detector comes with a **before/after code example** in the help\r\npanel. Clicking a finding copies a **Markdown block ready for Claude AI**\r\nto the clipboard.\r\n\r\nFor the **22 DFM-specific detectors** (DFM-DeadEventHandler,\r\nDFM-HardcodedDBCredentials, DFM-CircularMasterDetail,\r\nDFM-MissingRequiredFieldBinding, DFM-SQLFromTEditText, …) and their\r\nfix hints: see [DETECTORS.md](DETECTORS.md).\r\n\r\nFull status of all 50 Sonar rules: see [DETECTORS.md](DETECTORS.md).\r\n\r\n---\r\n\r\n## Usage\r\n\r\n### Buttons (left to right)\r\n\r\n| Button | Function |\r\n|--------|----------|\r\n| **Folder picker** (`...`) | Choose the project folder |\r\n| **Settings...** | Open `analyser.ini` — VCS settings, custom LeakyClasses (see [BRANCH_CHANGES.md](BRANCH_CHANGES.md)) |\r\n| **Ignore...** | Open `ignore.txt` — file/folder exclusion list |\r\n| **Start analysis** | Recursive folder scan |\r\n| **Current file** | Just the `.pas` file currently open in the editor |\r\n| **Branch-Changes** | Only files changed in Git/SVN (see [BRANCH_CHANGES.md](BRANCH_CHANGES.md)) |\r\n| **Cancel** | Aborts a running analysis |\r\n\r\n![SCA IDE plugin in action — buttons, grid, hover overlays](docs/PlugInSca.gif)\r\n\r\n### Per-detector configuration\r\n\r\nThere are no toggle checkboxes in the toolbar. All optional detector\r\nbehaviour is configured via `analyser.ini` (see _Configuration files_\r\nbelow) — open it through the **Settings…** button, edit, save, click\r\n**Start analysis** again. Settings are reloaded on every run, no IDE\r\nrestart required.\r\n\r\n### Stat cards\r\n\r\nTwo card rows above the grid show how findings are distributed:\r\n\r\n- **By severity**: Errors / Warnings / Hints / Security risks / Read errors\r\n- **By type**: Code Smell / Bug / Vulnerability / Security Hotspot / Code Duplication / Read errors\r\n\r\nBoth rows are guaranteed to add up to the same total.\r\n\r\n### Filter\r\n\r\n- **Severity / type dropdowns**: narrow the grid down to a single category.\r\n- **Profile dropdown**: switch the active rule-set on the fly. Bundled\r\n  profiles: `ide-fast` (IDE default — bugs + vulns only), `default` (every\r\n  detector), `strict` (default + `UnusedUses`), `security` (vulns +\r\n  hotspots only), `bugs-only`, `code-quality`, `dfm-only`. Profiles\r\n  live in `rules/sca-rules.json` under `profiles` and the dropdown is\r\n  populated from there — drop your own profile in the JSON and it\r\n  shows up. Selection is persisted to `[Rules] IdeProfile` and takes\r\n  effect at the next analysis run.\r\n- **Search box** (`Filter file / method / finding`): live filter across\r\n  every column.\r\n\r\n### Grid interaction\r\n\r\n| Action | Effect |\r\n|--------|--------|\r\n| **Click a row** | Finding is copied to the clipboard as a Markdown prompt for Claude AI. If a Quick-Fix provider exists for the rule (`RedundantBoolean`, `FreeAndNilHint`, `EmptyArgumentList`, `AssignedAndAssignedNil`), the fixed line is prepended to the clipboard as a paste-ready code block. If the file is open in the IDE, a 3 px stripe is painted on the left edge of the corresponding line in the editor. |\r\n| **Double-click / Enter** | Open the file in the IDE, jump to the finding line, paint the line marker |\r\n| **Ctrl+Alt+F** | **Apply Quick-Fix in editor** (in-place replace via IOTAEditWriter, Ctrl+Z to undo). Only for rules with a registered provider. Status-bar reports the result. |\r\n| **Ctrl+Alt+S** | **Insert suppression marker** above the finding line: `// noinspection \u003cRuleName\u003e`. Next analysis run filters the finding. Ctrl+Z to revert. |\r\n| **F3 / Shift+F3** | Next / previous finding in the grid |\r\n| **Hover (file column)** | Tooltip with the full file path (100 ms delay) |\r\n| **Click a column header** | Sort by that column |\r\n| **3 px stripe on the left edge** of the grid row | Severity accent (red / orange / green / blue) |\r\n\r\nThe right-side **help panel** with before/after code blocks is shown only\r\nwhen the IDE plugin window is **floating** — when docked into a side\r\nbar / tab the panel auto-hides and the grid takes the full width\r\n(re-appears within ~250 ms after un-docking).\r\n\r\n![SCA IDE plugin docked into a side panel — auto-hidden help panel, full-width grid](docs/PlugInDockedSca.gif)\r\n\r\n### Export\r\n\r\n| Button | Format | Content |\r\n|--------|--------|---------|\r\n| **JSON** | `.json` | All findings as an array |\r\n| **CSV** | `.csv` | Excel-friendly (semicolon-separated) |\r\n| **HTML report** | `.html` | Self-contained report with sort, filter, code snippets, before/after. Click a severity badge to filter — also hides files in the dropdown that have no findings of that severity (combinable with the file dropdown filter, AND-linked) |\r\n| **Jira** | Clipboard | Wiki markup ready to paste into a Jira ticket (filtered to one file) |\r\n| **Clipboard** | Clipboard | Plain text with before/after (filtered to one file) |\r\n\r\n---\r\n\r\n## Language / localisation\r\n\r\nThe UI source language is **English**. UI strings are wrapped with the\r\n`_('…')` macro from `uLocalization.pas`, which routes through dxgettext\r\n(GNU gettext for Delphi) when activated.\r\n\r\n### Switching the language\r\n\r\n| State | Effect |\r\n|-------|--------|\r\n| **Default (no dxgettext)** | UI displays the source strings as-is — English |\r\n| **dxgettext activated, no `SetLanguage` call** | UI follows system locale via `gnugettext.UseLanguageFromSysLocale` |\r\n| **`uLocalization.SetLanguage('de')`** | UI switches to German via `i18n/de.po` |\r\n| **`uLocalization.SetLanguage('fr')`** | UI switches to French via `i18n/fr.po` |\r\n| **`uLocalization.SetLanguage('en')`** | UI forced to English |\r\n\r\nTo set the language at startup, call `SetLanguage` early in\r\n`TAnalyserDockableForm.FrameCreated` (IDE plugin) or in the standalone's\r\n`TForm2.FormCreate`:\r\n\r\n```pascal\r\nuses uLocalization;\r\n\r\nSetLanguage('de');   // 'de' / 'en' / 'fr' / '' (= system default)\r\n```\r\n\r\n### Where translations live\r\n\r\n| Path | Purpose |\r\n|------|---------|\r\n| `i18n/template.pot` | Source-language template (English) |\r\n| `i18n/de.po` | German translation |\r\n| `i18n/fr.po` | French translation |\r\n| `i18n/en.po` | English baseline (identity) |\r\n| `locale/\u003clang\u003e/LC_MESSAGES/default.mo` | Compiled binary, loaded at runtime |\r\n\r\nThe `.po` files are plain text and Git-friendly; edit them with\r\n[poEdit](https://poedit.net/) or a regular editor.\r\n\r\n### Activating dxgettext (one-time setup)\r\n\r\nWithout dxgettext installed, the wrapper is a passthrough — every `_()`\r\ncall returns the source string unchanged, so the UI stays English no\r\nmatter what `SetLanguage` is called with.\r\n\r\nTo get real translations:\r\n\r\n1. Clone \u003chttps://github.com/sjrd/dxgettext\u003e\r\n2. Add the `dxgettext/Source/` folder to `DCC_UnitSearchPath` of the\r\n   IDE plugin and the standalone EXE\r\n3. Add `{$DEFINE USE_GETTEXT}` in the `.dpk` (or in **Project Options →\r\n   Conditional Defines**)\r\n4. Compile every `.po` to `.mo`:\r\n   ```\r\n   msgfmt i18n/de.po -o locale/de/LC_MESSAGES/default.mo\r\n   msgfmt i18n/fr.po -o locale/fr/LC_MESSAGES/default.mo\r\n   ```\r\n5. Place the `locale/` folder next to the BPL/EXE\r\n\r\nFull step-by-step instructions: [I18N.md](I18N.md).\r\n\r\n---\r\n\r\n## Theme integration\r\n\r\nThe plugin tracks the active Delphi IDE theme through several\r\nmechanisms:\r\n\r\n- **`StyleServices.GetSystemColor`** in custom drawing (OnDrawCell, TTilePanel.Paint)\r\n- **`clBtnFace` / `clWindow` / `clBtnText`** as property values (auto-themed via VCL Styles)\r\n- **`IOTAIDEThemingServices.ApplyTheme`** when the frame is hosted\r\n- **`INTAIDEThemingServicesNotifier`** for live theme changes\r\n- **`CM_STYLECHANGED`** plus a **`SetParent` override** as additional triggers\r\n\r\nSeverity background colors are blended at paint time from the themed\r\n`clWindow` base mixed with a saturated accent color, so the same code\r\nworks in any theme without separate light/dark tables.\r\n\r\n**Known limitation**: in floating mode the plugin window does not pick\r\nup runtime IDE theme changes reliably — `INTACustomDockableForm` exposes\r\nno official hook for re-applying the theme on the wrapper form.\r\nWorkaround: dock the plugin, or close and re-open the window after\r\nswitching themes.\r\n\r\n---\r\n\r\n## Using the analyser with Git and SVN\r\n\r\nThe analyser **auto-detects** the VCS system based on the project\r\ndirectory (looks for `.git/` or `.svn/` markers). Custom rules and\r\nall detector configuration are **VCS-agnostic** — the same workflow\r\nworks with both systems.\r\n\r\n### Auto-detection\r\n\r\n| Marker in project path | Detected as | Backend CLI |\r\n|---|---|---|\r\n| `.git/` (or any parent contains `.git/`) | Git | `git diff` + `git status` |\r\n| `.svn/` | SVN | `svn status` + `svn diff` |\r\n| neither | None | `--branch` disabled, `--full` works |\r\n\r\nThe VCS executable is located via `PATH`, then typical install paths\r\n(TortoiseGit, TortoiseSVN, Git for Windows, ...). Override via\r\n`analyser.ini` (see below).\r\n\r\n### Using with Git\r\n\r\n**Plugin/GUI**: point the project path at the Git working tree, then\r\nclick **Branch-Changes**. The analyser determines:\r\n- Modified `.pas` files between `BaseBranch` and `HEAD` (committed)\r\n- Plus uncommitted working-tree modifications (when `IncludeWorkingTree=1`)\r\n\r\n**CLI**:\r\n```powershell\r\nanalyser.d12.exe --path D:\\my-git-repo --branch --report-sarif sca.sarif\r\n\r\n# Optional: narrow the rule-set via profile + minimum severity\r\nanalyser.d12.exe --path . --profile security --report-sarif sec.sarif\r\nanalyser.d12.exe --path . --profile bugs-only --min-severity warning\r\n```\r\n\r\n`--profile \u003cname\u003e` accepts any profile from `rules/sca-rules.json`\r\n(bundled: `default`, `ide-fast`, `strict`, `security`, `bugs-only`,\r\n`code-quality`, `dfm-only`). `--min-severity hint|warning|error` skips\r\ndetectors below the threshold. Both flags override `[Rules]` in `analyser.ini`.\r\n\r\n**`analyser.ini` settings for Git**:\r\n```ini\r\n[Repo]\r\nBaseBranch=develop          ; empty = auto: origin/HEAD -\u003e main -\u003e master\r\nIncludeWorkingTree=1        ; 1 = include uncommitted changes, 0 = committed only\r\n\r\n[Paths]\r\nGitExe=C:\\custom\\git\\bin\\git.exe   ; empty = auto-detection\r\n```\r\n\r\n### Using with SVN\r\n\r\n**Plugin/GUI**: identical to Git — pick the working-copy path, click\r\n**Branch-Changes**. Since SVN has no real \"branch\" concept in a\r\nworking copy, branch mode here returns:\r\n- All uncommitted changes (`svn status` output: M/A/R/D/?)\r\n- Optionally extended with committed differences since BASE revision\r\n\r\nIdeal as a **pre-commit hook**: it checks exactly what would go into\r\nthe next `svn commit`.\r\n\r\n**CLI**:\r\n```powershell\r\nanalyser.d12.exe --path D:\\my-svn-wc --branch --report-sarif sca.sarif\r\n```\r\n\r\n**`analyser.ini` settings for SVN**:\r\n```ini\r\n[Repo]\r\nBaseBranch=trunk            ; SVN: typically trunk (informational, no real diff)\r\nIncludeWorkingTree=1        ; include uncommitted changes\r\n\r\n[Paths]\r\nSvnExe=C:\\custom\\svn\\bin\\svn.exe   ; empty = auto: PATH + TortoiseSVN\r\n```\r\n\r\n**Auto-detected SVN paths**:\r\n1. `svn.exe` in PATH\r\n2. `C:\\Program Files\\TortoiseSVN\\bin\\svn.exe`\r\n3. `C:\\Program Files (x86)\\TortoiseSVN\\bin\\svn.exe`\r\n4. `C:\\Program Files\\Subversion\\bin\\svn.exe`\r\n\r\n### Custom rules with both VCS\r\n\r\nThe [custom-rule engine](examples/README.md) (YAML profiles) is\r\nVCS-independent — it just reads files. Recommended workflow for\r\n**both** VCS systems:\r\n\r\n1. Place `analyser-rules.yml` (or one of the profiles from\r\n   `examples/`) in the **project root** — Git/SVN version it along\r\n   with the source\r\n2. Reference it in `analyser.ini`:\r\n   ```ini\r\n   [Detectors]\r\n   CustomRulesFile=analyser-rules.yml   ; relative to project root\r\n   ```\r\n3. Plugin/GUI loads it automatically on the next analysis run\r\n\r\nThis way each project carries **its own ruleset in the repo** —\r\nteam-shared, versioned, reviewable in PR/MR diffs.\r\n\r\n### CI/CD integration\r\n\r\n**GitHub Actions** (Git): see template [`.github/workflows/sca.yml`](.github/workflows/sca.yml).\r\nSARIF upload appears as inline annotations in PRs.\r\n\r\n**GitLab CI / Jenkins / TeamCity / Azure DevOps**: same pattern —\r\nmake the tool available in the pipeline image, run `analyser.exe\r\n--path . --branch --report-sarif sca.sarif`, attach the artifact or\r\nprocess further (SARIF plugins available for most CI systems).\r\n\r\n**SVN pre-commit hook** (server-side, Linux):\r\n```bash\r\n#!/bin/sh\r\n# /path/to/svn-repo/hooks/pre-commit\r\nREPOS=\"$1\"\r\nTXN=\"$2\"\r\n\r\n# Adjust tool path and working-copy mirror\r\nANALYSER=/opt/sca/analyser.d12.exe\r\nWC=/tmp/sca-precommit-$TXN\r\n\r\nsvn export \"$REPOS\" \"$WC\" -r \"$TXN\" --quiet\r\n\"$ANALYSER\" --path \"$WC\" --full --quiet\r\nEXIT=$?\r\nrm -rf \"$WC\"\r\nexit $EXIT\r\n```\r\n\r\nExit code mapping:\r\n- 0 = clean → commit allowed\r\n- 1 = hints only → commit allowed\r\n- 2 = warnings → commit allowed (or block via hook logic)\r\n- 3 = errors → **commit blocked**\r\n\r\n---\r\n\r\n## Configuration files\r\n\r\nMost settings can be edited through **Tools \u003e Options \u003e Third Party \u003e\r\nStatic Code Analyser** in the IDE plugin (live preview, theme-aware):\r\n\r\n![Tools \u003e Options page for Static Code Analyser inside the Delphi IDE](docs/OptionsSca.gif)\r\n\r\nThe same values persist to the INI files below — pick whichever is more\r\nconvenient. All under `%APPDATA%\\StaticCodeAnalyser\\`:\r\n\r\n| File | Content |\r\n|------|---------|\r\n| `analyser.ini` | All settings — VCS (BaseBranch, git/svn paths), detector toggles (`UsesCheck`, `IncludeTests`, `AutoDiscoverClasses`), custom `LeakyClasses` / `ExcludeLeakyClasses`, detector thresholds, UI language. The file is created on first start with self-documenting comments next to every option |\r\n| `ignore.txt` | File and directory patterns to skip during analysis |\r\n| `recent.ini` | Recently used project paths |\r\n| `LeakyClassesDiscover.log` | Output of `AutoDiscoverClasses=1` — discovered classes split into _instantiable_ (have ctor/dtor or `Create()` call) and _static-only candidates_. Manually copy the relevant ones into `LeakyClasses=` in `analyser.ini` |\r\n| `StaticCodeAnalyser_scan.log` | Diagnostic log: which file took how long |\r\n\r\n### Detector thresholds (all optional, in `[Detectors]`)\r\n\r\n| Key | Default | Effect |\r\n|-----|---------|--------|\r\n| `LongMethodMaxBodyLines` | 50 | `LongMethod` triggers when both body-line count AND statement count are above their thresholds |\r\n| `LongMethodMaxStatements` | 30 | (secondary threshold for `LongMethod`) |\r\n| `LongParamListMaxParams` | 5 | `\u003e N` parameters → refactoring hint |\r\n| `DeepNestingMaxDepth` | 4 | `\u003e N` nested control structures |\r\n| `CyclomaticMax` | 10 | McCabe complexity `\u003e N` per method (counts `if`, `case` arm, `for`/`while`/`repeat`, `on` handler, `and`/`or`/`xor`) |\r\n| `DuplicateBlockMinLines` | 8 | minimum normalised line count for duplicate-block detection |\r\n| `MaxFileMB` | 5 | files larger than that are skipped (OOM guard for generated code) |\r\n| `MagicNumberTrivials` | `0,1,2,-1,10,100` | numbers exempt from `MagicNumber` detection |\r\n| `UsesCheck` | 0 | `UnusedUses` detector (off by default — can produce false positives) |\r\n| `IncludeTests` | 0 | include `uTest*.pas`, `*_Tests.pas`, `TestProject*.dpr`, `/tests/` directories |\r\n| `AutoDiscoverClasses` | 0 | scan project AST for custom classes that need `Free` and add them to `LeakyClasses` |\r\n| `LeakyClasses` | _(empty)_ | comma-separated list of additional classes to track |\r\n| `ExcludeLeakyClasses` | _(empty)_ | comma-separated list of classes to NOT track even if they're in the defaults |\r\n\r\n### Live-Watch (IDE plugin only) — ⚠️ RISKY\r\n\r\nClicking **Current file** in the IDE plugin activates a single-file live watch\r\non exactly that file: every save (300 ms debounced) and every edit (1000 ms\r\ndebounced) re-runs the analysis for THIS file in a background thread. Switching\r\ntabs to another file changes nothing; clicking **Current file** again moves the\r\nwatch to the new file. Bulk paths (**Run analysis**, **Branch changes**)\r\nexplicitly deactivate the watch. There is no INI flag for this.\r\n\r\n\u003e ⚠️ **Infinite-loop risk.** There is currently **no re-entrancy guard** for\r\n\u003e overlapping worker spawns. If the worker takes longer than the edit debounce\r\n\u003e (1000 ms) and the user keeps typing, the worker backlog will grow rather\r\n\u003e than shrink. Additionally (Delphi-version dependent), an editor repaint\r\n\u003e following a findings update can be re-interpreted as `Modified` — edit/save\r\n\u003e paths can then re-trigger each other. The only safety today is the\r\n\u003e generation counter (drops _late_ results, but does not prevent overlapping\r\n\u003e spawns). Add a re-entrancy guard + hard cap before broader use\r\n\u003e (`TODO.md` -\u003e _Single-File-Live-Watch_).\r\n\r\n---\r\n\r\n## Suppression\r\n\r\nSilence individual findings inline:\r\n\r\n```pascal\r\n// noinspection MemoryLeak\r\nlist := TStringList.Create;\r\n\r\n// noinspection NilDeref, DivByZero\r\nDoSomethingRisky;\r\n\r\n// noinspection All\r\n// suppress every check on the next line\r\n```\r\n\r\nRecognised category names (one per registered detector — single source of\r\ntruth is `KIND_META` in `uSCAConsts.pas`):\r\n\r\n`MemoryLeak`, `EmptyExcept`, `SQLInjection`, `HardcodedSecret`,\r\n`FormatMismatch`, `FileReadError`, `UnusedUses`, `NilDeref`,\r\n`MissingFinally`, `DivByZero`, `DeadCode`, `LongMethod`, `LongParamList`,\r\n`MagicNumber`, `DuplicateString`, `HardcodedPath`, `DebugOutput`,\r\n`DeepNesting`, `TodoComment`, `EmptyMethod`, `DuplicateBlock`, `All`.\r\n\r\n---\r\n\r\n## Ownership transfer (no MemoryLeak warning)\r\n\r\nThese patterns are recognised as ownership hand-off and don't trigger a\r\nMemoryLeak finding:\r\n\r\n| Pattern | Meaning |\r\n|---------|---------|\r\n| `Result := varName` | Function returns ownership to its caller |\r\n| `varName.Parent := winControl` | VCL: TWinControl frees its `Controls[]` children |\r\n| `varName := X.Add(...)` | Borrowed return — item lives in container's `OwnsObjects` list |\r\n| `varName := X.AddChild(...)` | AST / DOM tree: child owned by parent |\r\n| `varName := X.AddNode(...)` | TTreeView etc. |\r\n| `varName := X.AppendChild(...)` | XML-DOM / IXMLNode |\r\n| `FField := varName` | Var-to-field transfer — ownership leaves method scope |\r\n| `FField := varName as ISomething` | Interface-refcount keeps the object alive |\r\n| `inherited Create(varName, …)` | Parent constructor takes ownership |\r\n| `TAnyClass.Create(varName, …)` | Another constructor takes ownership |\r\n| `Container.Add(varName)` | TObjectList (etc.) takes ownership |\r\n| `Container.Add(key, varName)` | TObjectDictionary takes ownership |\r\n| `Container.AddObject(text, varName)` | TStringList with objects |\r\n| `Container.Insert(i, varName)` | TList.Insert |\r\n| `Container.Push(varName)` | TStack.Push |\r\n| `Container.Enqueue(varName)` | TQueue.Enqueue |\r\n\r\nFor **class fields**, the FieldLeak detector additionally recognises\r\nthe standard TComponent-owner pattern as no-leak:\r\n\r\n| Pattern | Meaning |\r\n|---------|---------|\r\n| `FField := X.Create(Self)` | TComponent owner: `inherited Destroy` calls `DestroyComponents` |\r\n| `FField := X.Create(AOwner)` | Owner forwarded from constructor parameter |\r\n| `FField := X.Create(Owner)` | Owner taken from existing field/property |\r\n\r\n---\r\n\r\n## Architecture\r\n\r\n```\r\nStaticCodeAnalyserIDE/                 IDE expert package (.dpk)\r\n  uIDEExpert.pas                       Wizard registration (IOTAMenuWizard)\r\n  uIDEAnalyserForm.pas                 Dockable window (TFrame) - main shell:\r\n                                       filters, stats grid, sort, export,\r\n                                       Claude prompt copy, lifecycle sentinel\r\n  uIDELineHighlighter.pas              3 px red stripe in the IDE editor\r\n                                       gutter on the offending line\r\n  uIDEMessages.pas                     Hand-off into the IDE Messages tab\r\n  uIDEWatchMode.pas                    Single-file live watch (Current file)\r\n                                       save 300 ms / edit 1000 ms debounced\r\n                                       ⚠️ no re-entrancy guard - see README\r\n  uIDEStatsTiles.pas                   Sonar-style tile row builder\r\n  uIDEHelpPanel.pas                    Right-side help panel with before/after,\r\n                                       auto-hide when docked\r\n  uIDEExportMenu.pas                   Export dropdown (JSON/CSV/HTML/Jira)\r\n  uIDEEditorIntegration.pas            ToolsAPI wrappers: current .pas file,\r\n                                       project dir, OpenFileAtLine\r\n  uIDEStatusBar.pas                    Three-panel status bar\r\n                                       (findings / progress / mode)\r\n  uIDEThemeIntegration.pas             IDE-theme notifier + ApplyTheme refresh\r\n  uIDEAnalyseProgress.pas              Busy-state controller\r\n                                       (Begin/EndRun, Cancel-flag)\r\n\r\nStaticCodeAnalyserForm/sources/        Analysis engine (shared by standalone + IDE plugin)\r\n  Common/\r\n    uSCAConsts.pas                     TFindingKind + KIND_META single source\r\n                                       of truth (Sonar category mapping)\r\n    uMethodd12.pas                     TLeakFinding record + helpers\r\n    uRecentPaths.pas                   recent.ini handling\r\n    uRegExMatches.pas                  shared regex helpers\r\n    uDetectorUtils.pas                 IsIdentChar, IsWholeWord helpers\r\n    uCollectValues.pas                 AST literal-value collection\r\n\r\n  UI/\r\n    uAnalyserPalette.pas               Central colour constants\r\n    uAnalyserTypes.pas                 TFindingSeverity enum + conversions\r\n    uAnalyserTheme.pas                 SeverityBg, SeverityAccent, BlendColor\r\n    uFindingGridRenderer.pas           StringGrid OnDrawCell logic\r\n    uFindingFilter.pas                 Severity/type/search filter pipeline\r\n    uLocalization.pas                  dxgettext wrapper (_('…') macro)\r\n\r\n  Parsing/\r\n    uLexer.pas                         Tokeniser, watchdog (200k tokens)\r\n    uParser2.pas                       Recursive-descent parser with\r\n                                       forward-progress guarantee\r\n    uAstNode.pas                       AST with FindAll / FindFirst lookup\r\n\r\n  Infrastructure/\r\n    uStaticAnalyzer2.pas               Orchestrates the ~130 Pascal detectors per file\r\n    uStaticFiles.pas                   Recursive file scan, tick callback,\r\n                                       cancel support, symlink protection\r\n    uIgnoreList.pas                    ignore.txt + test filter\r\n    uVcsChanges.pas                    Git/SVN diff via CreateProcess + pipe\r\n    uRepoSettings.pas                  analyser.ini (BaseBranch, exe paths)\r\n    uSuppression.pas                   // noinspection markers\r\n    uExport.pas                        JSON / CSV / Jira / clipboard\r\n    uExportHtml.pas                    Self-contained HTML report\r\n\r\n  Output/\r\n    uClaudePrompt.pas                  AI Markdown prompt generator\r\n    uFixHint.pas                       Before/after example per finding type\r\n\r\n  Detectors/\r\n    uLeakDetector2.pas                 MemoryLeak (local-var, AST-based)\r\n    uFieldLeak.pas                     Class-field leak (Create / Destroy)\r\n    uCodeSmells2.pas                   EmptyExcept\r\n    uSQLInjection.pas                  + uSQLInjectionScore.pas (scoring)\r\n    uHardcodedSecret.pas, uHardcodedPath.pas\r\n    uFormatMismatch.pas, uUnusedUses.pas\r\n    uNilDeref.pas, uMissingFinally.pas\r\n    uDivByZero.pas, uDeadCode.pas\r\n    uLongMethod.pas, uLongParamList.pas\r\n    uMagicNumbers.pas, uDuplicateString.pas\r\n    uDuplicateBlock.pas\r\n    uDebugOutput.pas, uDeepNesting.pas\r\n    uTodoComment.pas, uEmptyMethod.pas\r\n    uCustomClassDiscovery.pas          AutoDiscoverClasses helper\r\n                                       (not a detector — feeds LeakyClasses)\r\n```\r\n\r\n### Data flow\r\n\r\n```\r\nFile → Lexer → Parser2 → AST (TAstNode)\r\n                            │\r\n                            ├── 21 detectors run in parallel (try/except per detector)\r\n                            │       each emits TLeakFinding\r\n                            │\r\n                            └── TSuppression strips noinspection markers\r\n                                        │\r\n                                        └── TObjectList\u003cTLeakFinding\u003e\r\n                                                │\r\n                                                └── PopulateFindings →\r\n                                                    Stat cards + grid + export\r\n```\r\n\r\n---\r\n\r\n## Performance\r\n\r\nFor a typical 1 000-unit repository:\r\n\r\n| Phase | Per file | 1 000 files |\r\n|-------|----------|-------------|\r\n| Folder scan | — | 1–3 s |\r\n| Lexer | ~5–15 ms | ~10 s |\r\n| Parser2 | ~10–50 ms | ~30 s |\r\n| ~130 Pascal detectors | ~10–60 ms | ~50 s |\r\n| DFM parser + 22 DFM detectors (per `.dfm`) | ~5–20 ms | ~5–10 s |\r\n| Suppression sweep | — | \u003c1 s |\r\n| **Total** | **~30–100 ms** | **~60–90 s** |\r\n\r\nFor incremental re-scans, **use Branch-Changes instead of a full scan**\r\n— typically 200 ms to 3 s. See [BRANCH_CHANGES.md](BRANCH_CHANGES.md).\r\n\r\n### Robustness\r\n\r\n- **Watchdog**: 200k-token limit per file — pathological inputs are\r\n  aborted in under a second instead of hanging.\r\n- **GuardAdvance**: forward-progress guarantee in every outer parser loop.\r\n- **Real-world Delphi syntax coverage**: the parser handles `interface`\r\n  type declarations, generic types/methods (`TFoo\u003cT\u003e`, `function Get\u003cT\u003e: T;`),\r\n  `packed record` / `packed class`, local `label` sections, `record helper for X`\r\n  / `class helper for X`, and IFDEF-conditional method headers without\r\n  losing method bodies — important for real-world codebases (mORMot2, etc.).\r\n- **`MaxFileMB` (default 5 MB)**: oversized files are reported immediately\r\n  as `FileError`. Configurable in `analyser.ini`.\r\n- **MAX_DEPTH = 32**: protection against symlink loops.\r\n- **Cancel any time**: `EAbort` propagates cleanly through every layer.\r\n- **Per-detector try/except**: a crashing detector never blocks any of\r\n  the other forty.\r\n\r\n---\r\n\r\n## Test projects\r\n\r\n```\r\nStaticCodeAnalyserForm/tests/\r\n  TestProject.dpr                      DUnitX console runner\r\n  uTestAnalyserChecks.pas              ~290 tests in 26 fixtures\r\n                                       (one fixture per detector)\r\n  uTestTAstNode.pas                    AST helper tests\r\n  uTestPerformance.pas                 Throughput benchmarks\r\n                                       (tokens/ms, lines/ms)\r\n```\r\n\r\nTests run on DUnitX. In console mode the test project emits an NUnit\r\nXML report — ready to wire into CI.\r\n\r\n---\r\n\r\n## Requirements\r\n\r\n- Delphi 12 (Athens)\r\n- DUnitX (only for the test suite, not for the plugin itself)\r\n- Optional: Git for Windows or TortoiseSVN **with** CLI tools for the\r\n  Branch-Changes feature\r\n\r\n### Build targets\r\n\r\n| Target | Win32 | Win64 |\r\n|--------|-------|-------|\r\n| **IDE plugin** (`StaticCodeAnalyserIDE.dpk`) | ✅ required | ❌ — must stay 32-bit because RAD Studio 12 IDE itself is 32-bit and plugins inherit |\r\n| **Standalone EXE / CLI** (`analyser.d12.dproj`) | ✅ | ✅ |\r\n| **Test suite** (`TestProject.dproj`) | ✅ | _add platform if needed_ |\r\n\r\nThe standalone EXE compiles cleanly for both `Win32` and `Win64` —\r\nboth targets pass through the same detector engine and emit the\r\nsame SARIF/JSON/CSV/HTML reports. Choose `Win64` if you want a\r\nlarger heap (relevant only on multi-GB scans).\r\n\r\n---\r\n\r\n## Component overview\r\n\r\n| Component | Path | Purpose |\r\n|-----------|------|---------|\r\n| **Standalone EXE** | `StaticCodeAnalyserForm/analyser.d12.dproj` | Folder/file scan outside the IDE |\r\n| **IDE plugin** | `StaticCodeAnalyserIDE/StaticCodeAnalyserIDE.dpk` | Main feature — dockable tool window with the full feature set |\r\n\r\nBoth share the analysis engine in `StaticCodeAnalyserForm/sources/`.\r\n\r\n---\r\n\r\n## Documentation\r\n\r\nThe repository contains three Markdown documents per language. They\r\ncomplement each other, so each one stands on its own:\r\n\r\n| File | Content | When to consult |\r\n|------|---------|-----------------|\r\n| [README.md](README.md) | **Overview** — what the plugin does, how to use it, architecture, performance, suppression, theme integration | Default starting point for everything except the two specialised topics below |\r\n| [DETECTORS.md](DETECTORS.md) | **Canonical detector list** — all 50 Sonar rules plus 3 bonus detectors with status (✅ implemented / 🟡 partial / 🔲 open), description and the responsible unit | When you want to know which rule is implemented, what exactly it checks, or which detector is up next |\r\n| [BRANCH_CHANGES.md](BRANCH_CHANGES.md) | **VCS / Branch-Changes feature** — how the `Branch-Changes` button works, Git/SVN setup, Tortoise compatibility, `analyser.ini` configuration, troubleshooting for repo detection | When the Branch-Changes button isn't doing what you expect, or you want to fine-tune the VCS setup |\r\n\r\nConvention: `README.md` is broad; the other two are deep and focused on\r\none aspect. Whenever a section in the README grows too large, it gets\r\nmoved into its own dedicated file (which is exactly what happened with\r\nthe Branch-Changes content).\r\n\r\n🇩🇪 German versions: [README_de.md](README_de.md), [DETECTORS_de.md](DETECTORS_de.md), [BRANCH_CHANGES_de.md](BRANCH_CHANGES_de.md)\r\n\r\n---\r\n\r\n## Related projects and alternatives\r\n\r\nIf you are evaluating this project, you may also be looking at:\r\n\r\n- **SonarQube / SonarLint** — language coverage is broad but **Delphi /\r\n  Object Pascal is not supported out of the box**. This project is the\r\n  closest \"Sonar feel\" you can get for Delphi without writing a Sonar\r\n  plugin yourself. Same five categories (Bug / Vulnerability / Security\r\n  Hotspot / Code Smell / Code Duplication), same quality-score idea,\r\n  SARIF export for GitHub Code Scanning.\r\n- **FixInsight** (CodeHealer) — commercial, IDE-integrated. This project\r\n  is a **free, open-source FixInsight alternative** with comparable\r\n  detector coverage on Pascal plus a dedicated DFM scanner that\r\n  FixInsight does not ship.\r\n- **Pascal Analyzer (PAL)** — commercial. Overlapping detector set,\r\n  but no DFM-aware checks, no Claude AI hand-off, no SARIF.\r\n- **DFMCheck / GExperts DFM-Check** — single-purpose DFM linters. The\r\n  22 DFM detectors in this project are a superset (graph-based\r\n  cross-form analysis, repo-wide form index, Pascal-AST coupling).\r\n- **DCC32 hints / warnings** — built-in compiler diagnostics. Useful\r\n  but limited to syntactic and trivially-semantic checks; no\r\n  taxonomy, no AST queries, no security category.\r\n\r\n## Keywords\r\n\r\nDelphi static code analysis, Object Pascal linter, RAD Studio plugin,\r\nDelphi 12 Athens, Delphi IDE plugin, ToolsAPI, DFM analyzer, form file\r\nlinter, Pascal AST, SonarQube alternative for Delphi, FixInsight\r\nalternative, Pascal Analyzer alternative, Delphi memory-leak detector,\r\nSQL injection detector for Delphi, hardcoded secret scanner, Delphi\r\ncode smell, Delphi code duplication, McCabe complexity Delphi,\r\nSARIF Delphi, Branch-Changes incremental scan, Git diff Delphi,\r\nSVN diff Delphi, Claude AI prompt, Delphi code review automation,\r\nTADOQuery security, TFDQuery security, TClientDataSet provider chain,\r\nTDataSetProvider audit, master-detail circular detection, dead event\r\nhandler detection, untranslated Caption detector, dxgettext audit,\r\nTEdit.Text SQL injection, hardcoded DB credentials in DFM, Pascal lint\r\nCI/CD, GitHub Actions Delphi SARIF, pre-commit hook Delphi.\r\n\r\n---\r\n\r\n## License\r\n\r\nThis project is released under the **MIT License** — see [LICENSE](LICENSE)\r\nfor the full text.\r\n\r\n```\r\nCopyright (c) 2026 Nicolas Gerlach\r\n```\r\n\r\nIn short:\r\n\r\n- ✅ Free to use, copy, modify, merge, publish, distribute and sublicense\r\n- ✅ Free for commercial use\r\n- ✅ No warranty — the software is provided \"as is\"\r\n- ℹ️ The copyright notice and the license text must be kept in copies or\r\n  substantial portions of the software\r\n\r\n---\r\n\r\n## Support\r\n\r\nDonate link is at the top of this README — thanks!\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnrodear%2Fstaticcodeanalyser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnrodear%2Fstaticcodeanalyser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnrodear%2Fstaticcodeanalyser/lists"}