nrodear/StaticCodeAnalyser
GitHub: nrodear/StaticCodeAnalyser
专为 RAD Studio 12 设计的 Delphi 静态代码分析工具,基于 AST 检测内存泄漏、安全漏洞和代码异味,以 IDE 插件形式运行并支持 AI 辅助修复。
Stars: 11 | Forks: 2
# Delphi 静态代码分析工具
[](LICENSE)
[](https://paypal.me/nrodear)
**Delphi 静态代码分析工具** 和 **linter**,适用于 **RAD Studio 12 (Athens)** —
以 **IDE 插件** 形式提供,包含一个可停靠的工具窗口以及一个 **独立的 Windows 应用程序**。
基于 AST 的分析,具有 **21 个检测器**,用于检测内存泄漏、SQL 注入、代码异味、
安全漏洞和代码重复。类似 Sonar 的分类,并提供质量评分。点击一次发现结果即可将适配 AI 的 Markdown 修复提示复制到
剪贴板。开源,采用 MIT 许可证。
🇩🇪 [德语版本](README_de.md)

## 此插件的功能
用一句话概括:**为 Delphi 项目提供类似 Sonar 的分析,无需配置 Sonar,在 IDE 内运行,并支持移交给 Claude AI 处理。**
| 功能 | 详细信息 |
|------------|---------|
| 🐛 **Bug 检测** | 21 个检测器针对每个 `.pas` 文件运行(MemoryLeak、NilDeref、DivByZero、FormatMismatch 等) |
| 🔐 **安全检查** | SQLInjection(基于评分)、HardcodedSecret、HardcodedPath |
| 🧹 **代码异味** | LongMethod、MagicNumber、EmptyExcept、MissingFinally、DeadCode、DuplicateString/Block |
| ⚡ **增量分析** | "Branch-Changes" 按钮:仅分析在 Git/SVN 分支中修改的文件 — 200 毫秒代替 60 秒 |
| 🤖 **Claude AI 提示词** | 点击一个发现结果 → 包含代码上下文 + 修改前/后的完整 Markdown 块被复制到剪贴板 |
| 📊 **类似 Sonar 的仪表板** | 网格上方的统计磁贴:错误 / 警告 / 提示 / Bug / 漏洞 / 代码质量评分 |
| 🎯 **过滤与排序** | 严重性下拉菜单、类型下拉菜单、实时搜索框、可点击的列标题 |
| 📤 **导出** | CSV、JSON、独立 HTML 报告、Jira wiki 标记、包含修改前/后内容的纯文本剪贴板 |
| 🔇 **抑制** | 在每行添加 `// noinspection MemoryLeak`,以及使用 `ignore.txt` 忽略整个文件 |
| 🌓 **主题感知** | 自动跟随当前活动的 IDE 主题(浅色 / 深色 / Mountain Mist / Carbon) |
| 💡 **修改前/后帮助** | 每个检测器在帮助面板中都有一个配对的“错误写法 / 正确写法”代码示例 |
## 主要功能
### 1. 静态代码分析(21 个检测器,Sonar 分类)
捕获 **Bug**(MemoryLeak、NilDeref、DivByZero、FormatMismatch),
**漏洞**(SQLInjection、HardcodedSecret),**安全热点**
(HardcodedPath),**代码异味**(LongMethod、MagicNumber、DeadCode、
EmptyExcept、MissingFinally 等)以及 **代码重复**(DuplicateString、
DuplicateBlock)。每个发现结果都在帮助面板中附带修改前/后的修复建议。
### 2. 增量 VCS 感知分析(Git + SVN)
跳过完整的项目扫描。**只需点击一次 `Branch-Changes`**:
分析器向 `git diff`(或 `svn status`)请求在您的分支中修改过的 `.pas` 文件,并仅对这些文件运行检测器。
对于典型的功能分支,**约 200 毫秒而不是 60 秒** — 成本足够低,
完全可以作为预提交门禁使用。配置保存在 `analyser.ini` 中。完整
详情请参阅 [BRANCH_CHANGES.md](BRANCH_CHANGES.md)。
### 3. AI 移交(一键 Claude 提示词)
点击网格中的一个发现结果行,剪贴板将被填充为一个
**现成的 Markdown 提示词**:发现元数据、代码上下文(±5
行,并在有问题的行上带有标记)以及修改前/后的修复建议。
使用 **Ctrl+V** 将其粘贴到 Claude 中 — AI 现在拥有了所需的一切,
可以建议具体的补丁。
## 快速开始
1. **构建并安装** 插件:打开 `StaticCodeAnalyserIDE\StaticCodeAnalyserIDE.dpk`,
运行 **Build**,然后运行 **Install**(在项目管理器中右键单击包 → **Install**,或从菜单中使用 **Component → Install Packages** 并选择该包)。如果不执行安装步骤,插件虽然能编译,但永远不会出现在 IDE 菜单中。
2. 在 Delphi 中:**View → Static Code Analysis Tool for Delphi** — 可停靠
窗口随即出现。
3. 选择一个项目路径 → 点击 **Start analysis**。
要仅对分支更改的文件进行增量扫描,请参阅
[BRANCH_CHANGES.md](BRANCH_CHANGES.md)。
## 检测内容(21 个检测器)
发现结果分为 **五个 Sonar 类别**之一:
| 类别 | 检测器 | 严重性 |
|----------|----------|----------|
| **Bug** | `MemoryLeak` (LeakDetector + FieldLeak) | Error / Warning |
| | `NilDeref` (nil 解引用) | Error |
| | `DivByZero` (除以零) | Error / Warning |
| | `FormatMismatch` (格式与参数数量不匹配) | Error |
| **漏洞** | `SQLInjection` (基于评分) | Error |
| | `HardcodedSecret` (API 密钥、密码) | Error |
| **安全热点** | `HardcodedPath` (`C:\…`, `/etc/…`) | Warning |
| **代码异味** | `EmptyExcept` (静默忽略异常) | Warning |
| | `MissingFinally` (在 finally 外调用 Free) | Warning |
| | `DeadCode` (在 exit/raise 后不可达) | Warning |
| | `UnusedUses` (可选,默认关闭) | Hint |
| | `LongMethod`, `LongParamList` | Hint |
| | `MagicNumber` (在 if 条件中) | Hint |
| | `DebugOutput` (`OutputDebugString` 等) | Warning |
| | `DeepNesting` | Warning |
| | `TodoComment` (TODO/FIXME/HACK) | Hint |
| | `EmptyMethod` | Hint |
| **代码重复** | `DuplicateString` (相同的字符串字面量,出现次数 ≥3) | Hint |
| | `DuplicateBlock` (行数 ≥ `DuplicateBlockMinLines`,默认 8 行完全相同) | Hint |
| **读取错误** | `FileReadError` (解析器挂起或文件过大) | Error |
每个检测器在帮助面板中都附带一个 **修改前/后的代码示例**。点击
一个发现结果会将一个 **适配 Claude AI 的 Markdown 块** 复制到剪贴板。
所有 50 条 Sonar 规则的完整状态:请参阅 [DETECTORS.md](DETECTORS.md)。
## 使用方法
### 按钮(从左到右)
| 按钮 | 功能 |
|--------|----------|
| **文件夹选择器** (`...`) | 选择项目文件夹 |
| **Settings...** | 打开 `analyser.ini` — VCS 设置、自定义 LeakyClasses(请参阅 [BRANCH_CHANGES.md](BRANCH_CHANGES.md)) |
| **Ignore...** | 打开 `ignore.txt` — 文件/文件夹排除列表 |
| **Start analysis** | 递归扫描文件夹 |
| **Current file** | 仅限编辑器中当前打开的 `.pas` 文件 |
| **Branch-Changes** | 仅限在 Git/SVN 中修改过的文件(请参阅 [BRANCH_CHANGES.md](BRANCH_CHANGES.md)) |
| **Cancel** | 中止正在运行的分析 |
### 每个检测器的配置
工具栏中没有切换复选框。所有可选的检测器
行为都通过 `analyser.ini` 进行配置(请参阅下文的 _配置文件_
) — 通过 **Settings…** 按钮打开它,进行编辑、保存,然后再次点击
**Start analysis**。每次运行时都会重新加载设置,无需重新启动 IDE。
### 统计卡片
网格上方的两行卡片显示了发现结果的分布情况:
- **按严重性分类**:错误 / 警告 / 提示 / 安全风险 / 读取错误
- **按类型分类**:代码异味 / Bug / 漏洞 / 安全热点 / 代码重复 / 读取错误
保证这两行数据的总和相同。
### 过滤
- **严重性/类型下拉菜单**:将网格缩小到单个类别。
- **搜索框** (`Filter file / method / finding`):跨所有列进行实时过滤。
### 网格交互
| 操作 | 效果 |
|--------|--------|
| **点击行** | 发现结果将作为适配 Claude AI 的 Markdown 提示词复制到剪贴板,**并且** — 如果文件已在 IDE 中打开 — 在编辑器相应行的左边缘会绘制一条 3 像素的红色条纹 |
| **双击** | 在 IDE 中打开文件,跳转到发现结果所在行,并绘制行标记 |
| **悬停(文件列)** | 显示包含完整文件路径的工具提示(延迟 100 毫秒) |
| **点击列标题** | 按该列排序 |
| 网格行的 **左边缘 3 像素条纹** | 严重性强调色(红 / 橙 / 绿 / 蓝) |
带有修改前/后代码块的右侧 **帮助面板** 仅在 IDE 插件窗口
**处于浮动状态**时显示 — 当停靠在侧边栏/选项卡中时,
面板会自动隐藏,并且网格占据整个宽度(在取消停靠后约 250 毫秒内会重新出现)。
### 导出
| 按钮 | 格式 | 内容 |
|--------|--------|---------|
| **JSON** | `.json` | 以数组形式表示的所有发现结果 |
| **CSV** | `.csv` | 适用于 Excel(以分号分隔) |
| **HTML report** | `.html` | 包含排序、过滤、代码片段、修改前/后内容的独立报告。点击严重性标记进行过滤 — 同时在下拉菜单中隐藏该严重性级别无发现结果的文件(可与文件下拉过滤器结合使用,AND 逻辑) |
| **Jira** | 剪贴板 | 可粘贴到 Jira 工单中的 Wiki 标记(过滤为单个文件) |
| **Clipboard** | 剪贴板 | 带有修改前/后内容的纯文本(过滤为单个文件) |
## 语言 / 本地化
UI 源语言为 **英语**。UI 字符串使用 `uLocalization.pas` 中的
`_('…')` 宏进行包裹,该宏在激活时会通过 dxgettext
(适用于 Delphi 的 GNU gettext)进行路由。
### 切换语言
| 状态 | 效果 |
|-------|--------|
| **默认(无 dxgettext)** | UI 按原样显示源字符串 — 英语 |
| **dxgettext 已激活,无 `SetLanguage` 调用** | UI 通过 `gnugettext.UseLanguageFromSysLocale` 跟随系统区域设置 |
| **`uLocalization.SetLanguage('de')`** | UI 通过 `i18n/de.po` 切换为德语 |
| **`uLocalization.SetLanguage('fr')`** | UI 通过 `i18n/fr.po` 切换为法语 |
| **`uLocalization.SetLanguage('en')`** | UI 强制切换为英语 |
要在启动时设置语言,请在
`TAnalyserDockableForm.FrameCreated`(IDE 插件)或独立程序的
`TForm2.FormCreate` 中尽早调用 `SetLanguage`:
```
uses uLocalization;
SetLanguage('de'); // 'de' / 'en' / 'fr' / '' (= system default)
```
### 翻译文件存放位置
| 路径 | 用途 |
|------|---------|
| `i18n/template.pot` | 源语言模板(英语) |
| `i18n/de.po` | 德语翻译 |
| `i18n/fr.po` | 法语翻译 |
| `i18n/en.po` | 英语基准(恒等映射) |
| `locale//LC_MESSAGES/default.mo` | 编译后的二进制文件,在运行时加载 |
`.po` 文件是纯文本格式且对 Git 友好;可以使用
[poEdit](https://poedit.net/) 或常规文本编辑器进行编辑。
### 激活 dxgettext(一次性设置)
如果没有安装 dxgettext,包装器将作为直接通道 — 每个 `_()`
调用都会原样返回源字符串,因此无论调用 `SetLanguage` 时传入什么参数,
UI 都将保持英语。
要获取实际的翻译:
1. 克隆
2. 将 `dxgettext/Source/` 文件夹添加到 IDE 插件和独立 EXE 的 `D_UnitSearchPath` 中
3. 在 `.dpk` 中添加 `{$DEFINE USE_GETTEXT}`(或在 **Project Options → Conditional Defines** 中添加)
4. 将每个 `.po` 编译为 `.mo`:
msgfmt i18n/de.po -o locale/de/LC_MESSAGES/default.mo
msgfmt i18n/fr.po -o locale/fr/LC_MESSAGES/default.mo
5. 将 `locale/` 文件夹放置在 BPL/EXE 旁边
完整的分步说明:[I18N.md](I18N.md)。
## 主题集成
插件通过多种机制跟踪当前活动的 Delphi IDE 主题:
- 自定义绘制(OnDrawCell、TTilePanel.Paint)中的 **`StyleServices.GetSystemColor`**
- 作为属性值(通过 VCL Styles 自动应用主题)的 **`clBtnFace` / `clWindow` / `clBtnText`**
- 当承载 Frame 时的 **`IOTAIDEThemingServices.ApplyTheme`**
- 用于实时主题更改的 **`INTAIDEThemingServicesNotifier`**
- **`CM_STYLECHANGED`** 加上 **`SetParent` 重写** 作为附加触发器
严重性背景色在绘制时由带有主题的 `clWindow` 基色混合饱和强调色进行混合,因此相同的代码
无需单独的浅色/深色对照表即可在任何主题下工作。
**已知限制**:在浮动模式下,插件窗口无法可靠地捕获
运行时的 IDE 主题更改 — `INTACustomDockableForm` 没有暴露
用于在包装器表单上重新应用主题的官方钩子。
解决方法:停靠插件,或者在切换主题后关闭并重新打开窗口。
## 结合 Git 和 SVN 使用分析器
分析器基于项目目录(查找 `.git/` 或 `.svn/` 标记)**自动检测** VCS 系统。自定义规则和
所有检测器配置均 **与 VCS 无关** — 相同的工作流程
适用于这两个系统。
### 自动检测
| 项目路径中的标记 | 检测结果 | 后端 CLI |
|---|---|---|
| `.git/`(或任何父目录包含 `.git/`) | Git | `git diff` + `git status` |
| `.svn/` | SVN | `svn status` + `svn diff` |
| 均无 | None | `--branch` 被禁用,`--full` 可用 |
VCS 可执行文件通过 `PATH` 定位,然后是典型的安装路径
(TortoiseGit、TortoiseSVN、Git for Windows 等)。可通过
`analyser.ini` 进行覆盖(见下文)。
### 结合 Git 使用
**插件/GUI**:将项目路径指向 Git 工作树,然后
点击 **Branch-Changes**。分析器会确定:
- `BaseBranch` 和 `HEAD` 之间修改的 `.pas` 文件(已提交)
- 加上未提交的工作树修改(当 `IncludeWorkingTree=1` 时)
**CLI**:
```
analyser.d12.exe --path D:\my-git-repo --branch --report-sarif sca.sarif
```
**Git 的 `analyser.ini` 设置**:
```
[Repo]
BaseBranch=develop ; empty = auto: origin/HEAD -> main -> master
IncludeWorkingTree=1 ; 1 = include uncommitted changes, 0 = committed only
[Paths]
GitExe=C:\custom\git\bin\git.exe ; empty = auto-detection
```
### 结合 SVN 使用
**插件/GUI**:与 Git 相同 — 选择工作副本路径,点击
**Branch-Changes**。由于 SVN 在工作副本中没有真正的“分支”概念,此处的分支模式返回:
- 所有未提交的更改(`svn status` 输出:M/A/R/D/?)
- 可选扩展为自 BASE 版本以来的已提交差异
非常适合作为 **预提交钩子**:它会准确检查下一次 `svn commit` 将要提交的内容。
**CLI**:
```
analyser.d12.exe --path D:\my-svn-wc --branch --report-sarif sca.sarif
```
**SVN 的 `analyser.ini` 设置**:
```
[Repo]
BaseBranch=trunk ; SVN: typically trunk (informational, no real diff)
IncludeWorkingTree=1 ; include uncommitted changes
[Paths]
SvnExe=C:\custom\svn\bin\svn.exe ; empty = auto: PATH + TortoiseSVN
```
**自动检测的 SVN 路径**:
1. PATH 中的 `svn.exe`
2. `C:\Program Files\TortoiseSVN\bin\svn.exe`
3. `C:\Program Files (x86)\TortoiseSVN\bin\svn.exe`
4. `C:\Program Files\Subversion\bin\svn.exe`
### 结合两种 VCS 的自定义规则
[自定义规则引擎](examples/README.md)(YAML 配置文件)是
独立于 VCS 的 — 它只负责读取文件。针对 **两种** VCS 系统的推荐工作流程:
1. 将 `analyser-rules.yml`(或 `examples/` 中的某个配置文件)放置在 **项目根目录** 中 — Git/SVN 会将其与源代码一起进行版本控制
2. 在 `analyser.ini` 中引用它:
[Detectors]
CustomRulesFile=analyser-rules.yml ; 相对于项目根目录
3. 插件/GUI 会在下次分析运行时自动加载它
这样,每个项目都在代码库中拥有 **自己的规则集** —
可在团队间共享、进行版本控制,并可在 PR/MR 差异中进行审查。
### CI/CD 集成
**GitHub Actions** (Git):请参阅模板 [`.github/workflows/sca.yml`](.github/workflows/sca.yml)。
SARIF 上传将在 PR 中以内联注解的形式显示。
**GitLab CI / Jenkins / TeamCity / Azure DevOps**:相同模式 —
在流水线镜像中提供该工具,运行 `analyser.exe
--path . --branch --report-sarif sca.sarif`,附加构件或
进行进一步处理(大多数 CI 系统都有可用的 SARIF 插件)。
**SVN 预提交钩子**(服务器端,Linux):
```
#!/bin/sh
# /path/to/svn-repo/hooks/pre-commit
REPOS="$1"
TXN="$2"
# 调整工具路径与 working-copy 镜像
ANALYSER=/opt/sca/analyser.d12.exe
WC=/tmp/sca-precommit-$TXN
svn export "$REPOS" "$WC" -r "$TXN" --quiet
"$ANALYSER" --path "$WC" --full --quiet
EXIT=$?
rm -rf "$WC"
exit $EXIT
```
退出代码映射:
- 0 = 干净 → 允许提交
- 1 = 仅有提示 → 允许提交
- 2 = 警告 → 允许提交(或通过钩子逻辑阻止)
- 3 = 错误 → **提交被阻止**
## 配置文件
全部位于 `%APPDATA%\StaticCodeAnalyser\` 下:
| 文件 | 内容 |
|------|---------|
| `analyser.ini` | 所有设置 — VCS(BaseBranch、git/svn 路径)、检测器开关(`UsesCheck`、`IncludeTests`、`AutoDiscoverClasses`)、自定义 `LeakyClasses` / `ExcludeLeakyClasses`、检测器阈值、UI 语言。文件在首次启动时创建,每个选项旁边都带有自我说明的注释 |
| `ignore.txt` | 在分析期间要跳过的文件和目录模式 |
| `recent.ini` | 最近使用的项目路径 |
| `LeakyClassesDiscover.log` | `AutoDiscoverClasses=1` 的输出 — 将发现的类拆分为 _可实例化的_(具有构造函数/析构函数或 `Create()` 调用)和 _仅静态候选_。手动将相关的类复制到 `analyser.ini` 中的 `LeakyClasses=` 中 |
| `StaticCodeAnalyser_scan.log` | 诊断日志:每个文件花费了多长时间 |
### 检测器阈值(均为可选项,在 `[Detectors]` 中)
| 键 | 默认值 | 效果 |
|-----|---------|--------|
| `LongMethodMaxBodyLines` | 50 | 当主体行数和语句数均超过其阈值时,触发 `LongMethod` |
| `LongMethodMaxStatements` | 30 |(`LongMethod` 的辅助阈值) |
| `LongParamListMaxParams` | 5 | `> N` 个参数 → 重构提示 |
| `DeepNestingMaxDepth` | 4 | `> N` 层嵌套控制结构 |
| `CyclomaticMax` | 10 | 每个方法的 McCabe 复杂度 `> N`(计算 `if`、`case` 分支、`for`/`while`/`repeat`、`on` 处理器、`and`/`or`/`xor`) |
| `DuplicateBlockMinLines` | 8 | 用于重复块检测的最小规范化行数 |
| `MaxFileMB` | 5 | 超过此大小的文件将被跳过(针对生成代码的 OOM 保护) |
| `MagicNumberTrivials` | `0,1,2,-1,10,100` | 豁免 `MagicNumber` 检测的数字 |
| `UsesCheck` | 0 | `UnusedUses` 检测器(默认关闭 — 可能产生误报) |
| `IncludeTests` | 0 | 包含 `uTest*.pas`、`*_Tests.pas`、`TestProject*.dpr`、`/tests/` 目录 |
| `AutoDiscoverClasses` | 0 | 扫描项目 AST 以查找需要 `Free` 的自定义类,并将它们添加到 `LeakyClasses` |
| `LeakyClasses` | _(空)_ | 要跟踪的附加类的逗号分隔列表 |
| `ExcludeLeakyClasses` | _(空)_ | 即使在默认值中,也 **不** 被跟踪的类的逗号分隔列表 |
### 实时监视(仅限 IDE 插件) — ⚠️ 高风险
在 IDE 插件中点击 **Current file** 会针对该文件激活单文件实时监视:
每次保存(防抖 300 毫秒)和每次编辑(防抖 1000 毫秒)都会在后台线程中重新运行针对 **此** 文件的分析。切换
到另一个文件的选项卡不会产生任何影响;再次点击 **Current file** 会将
监视转移到新文件。批量路径操作(**Run analysis**、**Branch changes**)
会显式停用监视。此功能没有 INI 标志。
## 抑制
通过内联方式消除单个发现结果:
```
// noinspection MemoryLeak
list := TStringList.Create;
// noinspection NilDeref, DivByZero
DoSomethingRisky;
// noinspection All
// suppress every check on the next line
```
识别的类别名称(每个注册检测器对应一个 — 唯一真实来源是 `uSCAConsts.pas` 中的 `KIND_META`):
`MemoryLeak`、`EmptyExcept`、`SQLInjection`、`HardcodedSecret`、
`FormatMismatch`、`FileReadError`、`UnusedUses`、`NilDeref`、
`MissingFinally`、`DivByZero`、`DeadCode`、`LongMethod`、`LongParamList`、
`MagicNumber`、`DuplicateString`、`HardcodedPath`、`DebugOutput`、
`DeepNesting`、`TodoComment`、`EmptyMethod`、`DuplicateBlock`、`All`。
## 所有权转移(无 MemoryLeak 警告)
这些模式被识别为所有权移交,不会触发
MemoryLeak 发现结果:
| 模式 | 含义 |
|---------|---------|
| `Result := varName` | 函数将所有权返回给其调用者 |
| `varName.Parent := winControl` | VCL:TWinControl 释放其 `Controls[]` 子控件 |
| `varName := X.Add(...)` | 借用返回 — 项存在于容器的 `OwnsObjects` 列表中 |
| `varName := X.AddChild(...)` | AST / DOM 树:子节点由父节点拥有 |
| `varName := X.AddNode(...)` | TTreeView 等 |
| `varName := X.AppendChild(...)` | XML-DOM / IXMLNode |
| `FField := varName` | 变量到字段的转移 — 所有权离开方法作用域 |
| `FField := varName as ISomething` | 接口引用计数保持对象处于活动状态 |
| `inherited Create(varName, …)` | 父类构造函数接管所有权 |
| `TAnyClass.Create(varName, …)` | 另一个构造函数接管所有权 |
| `Container.Add(varName)` | TObjectList(等)接管所有权 |
| `Container.Add(key, varName)` | TObjectDictionary 接管所有权 |
| `Container.AddObject(text, varName)` | 带有对象的 TStringList |
| `Container.Insert(i, varName)` | TList.Insert |
| `Container.Push(varName)` | TStack.Push |
| `Container.Enqueue(varName)` | TQueue.Enqueue |
对于 **类字段**,FieldLeak 检测器还会将
标准的 TComponent 所有者模式识别为无泄漏:
| 模式 | 含义 |
|---------|---------|
| `FField := X.Create(Self)` | TComponent 所有者:`inherited Destroy` 调用 `DestroyComponents` |
| `FField := X.Create(AOwner)` | 所有者从构造函数参数转发 |
| `FField := X.Create(Owner)` | 所有者取自现有字段/属性 |
## 架构
```
StaticCodeAnalyserIDE/ IDE expert package (.dpk)
uIDEExpert.pas Wizard registration (IOTAMenuWizard)
uIDEAnalyserForm.pas Dockable window (TFrame) - main shell:
filters, stats grid, sort, export,
Claude prompt copy, lifecycle sentinel
uIDELineHighlighter.pas 3 px red stripe in the IDE editor
gutter on the offending line
uIDEMessages.pas Hand-off into the IDE Messages tab
uIDEWatchMode.pas Single-file live watch (Current file)
save 300 ms / edit 1000 ms debounced
⚠️ no re-entrancy guard - see README
uIDEStatsTiles.pas Sonar-style tile row builder
uIDEHelpPanel.pas Right-side help panel with before/after,
auto-hide when docked
uIDEExportMenu.pas Export dropdown (JSON/CSV/HTML/Jira)
uIDEEditorIntegration.pas ToolsAPI wrappers: current .pas file,
project dir, OpenFileAtLine
uIDEStatusBar.pas Three-panel status bar
(findings / progress / mode)
uIDEThemeIntegration.pas IDE-theme notifier + ApplyTheme refresh
uIDEAnalyseProgress.pas Busy-state controller
(Begin/EndRun, Cancel-flag)
StaticCodeAnalyserForm/sources/ Analysis engine (shared by standalone + IDE plugin)
Common/
uSCAConsts.pas TFindingKind + KIND_META single source
of truth (Sonar category mapping)
uMethodd12.pas TLeakFinding record + helpers
uRecentPaths.pas recent.ini handling
uRegExMatches.pas shared regex helpers
uDetectorUtils.pas IsIdentChar, IsWholeWord helpers
uCollectValues.pas AST literal-value collection
UI/
uAnalyserPalette.pas Central colour constants
uAnalyserTypes.pas TFindingSeverity enum + conversions
uAnalyserTheme.pas SeverityBg, SeverityAccent, BlendColor
uFindingGridRenderer.pas StringGrid OnDrawCell logic
uFindingFilter.pas Severity/type/search filter pipeline
uLocalization.pas dxgettext wrapper (_('…') macro)
Parsing/
uLexer.pas Tokeniser, watchdog (200k tokens)
uParser2.pas Recursive-descent parser with
forward-progress guarantee
uAstNode.pas AST with FindAll / FindFirst lookup
Infrastructure/
uStaticAnalyzer2.pas Orchestrates the 21 detectors per file
uStaticFiles.pas Recursive file scan, tick callback,
cancel support, symlink protection
uIgnoreList.pas ignore.txt + test filter
uVcsChanges.pas Git/SVN diff via CreateProcess + pipe
uRepoSettings.pas analyser.ini (BaseBranch, exe paths)
uSuppression.pas // noinspection markers
uExport.pas JSON / CSV / Jira / clipboard
uExportHtml.pas Self-contained HTML report
Output/
uClaudePrompt.pas AI Markdown prompt generator
uFixHint.pas Before/after example per finding type
Detectors/
uLeakDetector2.pas MemoryLeak (local-var, AST-based)
uFieldLeak.pas Class-field leak (Create / Destroy)
uCodeSmells2.pas EmptyExcept
uSQLInjection.pas + uSQLInjectionScore.pas (scoring)
uHardcodedSecret.pas, uHardcodedPath.pas
uFormatMismatch.pas, uUnusedUses.pas
uNilDeref.pas, uMissingFinally.pas
uDivByZero.pas, uDeadCode.pas
uLongMethod.pas, uLongParamList.pas
uMagicNumbers.pas, uDuplicateString.pas
uDuplicateBlock.pas
uDebugOutput.pas, uDeepNesting.pas
uTodoComment.pas, uEmptyMethod.pas
uCustomClassDiscovery.pas AutoDiscoverClasses helper
(not a detector — feeds LeakyClasses)
```
### 数据流
```
File → Lexer → Parser2 → AST (TAstNode)
│
├── 21 detectors run in parallel (try/except per detector)
│ each emits TLeakFinding
│
└── TSuppression strips noinspection markers
│
└── TObjectList
│
└── PopulateFindings →
Stat cards + grid + export
```
## 性能
对于典型的 1,000 个单元的代码库:
| 阶段 | 每个文件 | 1,000 个文件 |
|-------|----------|-------------|
| 文件夹扫描 | — | 1–3 秒 |
| 词法分析器 | ~5–15 毫秒 | ~10 秒 |
| Parser2 | ~10–50 毫秒 | ~30 秒 |
| 21 个检测器 | ~5–30 毫秒 | ~20 秒 |
| 抑制扫描 | — | <1 秒 |
| **总计** | **~30–100 毫秒** | **~60–90 秒** |
对于增量重新扫描,请使用 Branch-Changes 而不是完整扫描**
— 通常需要 200 毫秒到 3 秒。请参阅 [BRANCH_CHANGES.md](BRANCH_CHANGES.md)。
### 健壮性
- **看门狗**:每个文件有 20 万 token 的限制 — 病态输入将在不到一秒钟内被
中止,而不会挂起。
- **GuardAdvance**:在每个外部解析器循环中保证向前推进。
- **真实世界的 Delphi 语法覆盖范围**:解析器可处理 `interface`
类型声明、泛型类型/方法(`TFoo`、`function Get: T;`)、
`packed record` / `packed class`、局部 `label` 节、`record helper for X`
/ `class helper for X`,以及 IFDEF 条件方法头,而不会
丢失方法体 — 这对于真实的代码库(mORMot2 等)非常重要。
- **`MaxFileMB`(默认 5 MB)**:过大的文件会立即被报告为
`FileError`。可在 `analyser.ini` 中配置。
- **MAX_DEPTH = 32**:防止符号链接循环。
- **随时可取消**:`EAbort` 可以干净地穿透每一层进行传播。
- **每个检测器独立的 try/except**:一个崩溃的检测器永远不会阻塞
其他二十个。
## 测试项目
```
StaticCodeAnalyserForm/tests/
TestProject.dpr DUnitX console runner
uTestAnalyserChecks.pas ~290 tests in 26 fixtures
(one fixture per detector)
uTestTAstNode.pas AST helper tests
uTestPerformance.pas Throughput benchmarks
(tokens/ms, lines/ms)
```
测试在 DUnitX 上运行。在控制台模式下,测试项目会输出 NUnit
XML 报告 — 可随时接入 CI。
## 系统要求
- Delphi 12 (Athens)
- DUnitX(仅用于测试套件,插件本身不需要)
- 可选:Git for Windows 或 TortoiseSVN **及其** CLI 工具,用于 Branch-Changes 功能
## 组件概览
| 组件 | 路径 | 用途 |
|-----------|------|---------|
| **独立 EXE** | `StaticCodeAnalyserForm/analyser.d12.dproj` | 在 IDE 外部进行文件夹/文件扫描 |
| **IDE 插件** | `StaticCodeAnalyserIDE/StaticCodeAnalyserIDE.dpk` | 主要功能 — 具有完整功能集的可停靠工具窗口 |
两者共享 `StaticCodeAnalyserForm/sources/` 中的分析引擎。
## 文档
代码库中包含每种语言的三份 Markdown 文档。它们
互为补充,因此每份文档都可以独立阅读:
| 文件 | 内容 | 何时查阅 |
|------|---------|-----------------|
| [README.md](README.md) | **概述** — 插件的功能、使用方法、架构、性能、抑制、主题集成 | 除以下两个专业主题外的所有内容的默认起点 |
| [DETECTORS.md](DETECTORS.md) | **规范的检测器列表** — 所有 50 条 Sonar 规则及 3 个附加检测器,包含状态(✅ 已实现 / 🟡 部分实现 / 🔲 待开发)、描述和负责的单元 | 当您想了解哪条规则已实现、它具体检查什么,或者下一个要处理的检测器是哪个时 |
| [BRANCH_CHANGES.md](BRANCH_CHANGES.md) | **VCS / Branch-Changes 功能** — `Branch-Changes` 按钮的工作原理、Git/SVN 设置、Tortoise 兼容性、`analyser.ini` 配置、代码库检测的故障排除 | 当 Branch-Changes 按钮未按预期工作,或者您想微调 VCS 设置时 |
约定:`README.md` 涉及面广;另外两份则内容深入且专注于
某一个方面。每当 README 中的某一节变得过长时,它就会被
移至其专属文件中(Branch-Changes 内容正是如此处理的)。
🇩🇪 德语版本:[README_de.md](README_de.md)、[DETECTORS_de.md](DETECTORS_de.md)、[BRANCH_CHANGES_de.md](BRANCH_CHANGES_de.md)
## 许可证
本项目采用 **MIT 许可证** 发布 — 完整文本请参阅 [LICENSE](LICENSE)。
```
Copyright (c) 2026 Nicolas Gerlach
```
简而言之:
- ✅ 可自由使用、复制、修改、合并、发布、分发和再许可
- ✅ 可免费用于商业用途
- ✅ 无担保 — 本软件按“原样”提供
- ℹ️ 在本软件的副本或
大部分内容中必须保留版权声明和许可证文本
## 支持
捐赠链接位于本 README 的顶部 — 感谢!
标签:AI提示词, Bug检测, CISA项目, Claude AI, CSV, Delphi, DevSecOps, DOE合作, Git/SVN, HTML, IDE插件, JSON, Linter, MIT许可, Object Pascal, odt, pptx, RAD Studio 12, SAST, Sonar风格, SQL注入检测, 上游代理, 代码安全, 代码异味, 代码检查工具, 代码规范, 代码重复检测, 内存泄漏检测, 增量分析, 安全漏洞扫描, 导出报告, 应用程序安全测试, 开源, 抽象语法树, 漏洞枚举, 独立Windows应用程序, 盲注攻击, 硬编码密钥检测, 网络安全研究, 自动化payload嵌入, 质量评分, 错误基检测, 静态代码分析