Release 0.9.8 — Phase 1-4 + Hardening v3/v4 + FP-Reduction Sprint
Released: 2026-06-10
🇩🇪 Deutsche Version
This release closes the 0.9.8 development cycle that began on
2026-06-02. Three layers of work landed on top of v0.9.7:
- Scanner-Qualität Phases 1-4 (since v0.9.7) — 13 quick-wins,
confidence audit, A.5{$IFDEF}-Awareness, SCA165 UnusedSuppression,
Golden-Corpus regression suite, SARIFpartialFingerprintsfor
baseline diffs. - Hardening v3 + v4 (2026-06-07/08) — stack-overflow protection
for deep ASTs (8 iterative-DFS detector refactors + 32 MB stack
PE-patch), AST-Destroy reentrancy double-free fix, DFM
resource-wrapper ($FF $0A $00) support, IDE-plugin Win32-OOM fix
via FixHint memoize-cache, scan.log phase-tracking + skip-log. - FP-Reduction Sprint (2026-06-09) — self-scan FPs in four style
detectors reduced by 78% (67 → 15). Side-fix:FreeAndNil(Self.Field)
withSelf.-qualifier is now recognised as freeing.
Full real-world verification: 24 repos in
D:\git-sca-realworld\ scan in 411 s with 0 crashes and produce
1.055.283 findings at profile strict.
TL;DR Highlights (delta over the original Phase 1-only plan)
- DFM Resource-Wrapper-Format
$FF $0A $00now parses correctly —
GExperts jumps from 0 to 1.084 DFM-findings, JVCL DFM-coverage roughly
doubles. The previous code mis-classified these as text and silently
emitted nothing. - No more
EInvalidPointer/SCA006 mid-scan crash on second file
via the AST-Destroy reentrancy bug fix (5ecd498). - No more Win32
EOutOfMemoryin the IDE plugin's
HighlightAllFindingsInFileat ≥100k findings —TFixHintResolver
now caches by(Kind, Severity). - scan.log Phase-Tracking — every
Analyseabbruch:finding now
reveals the last successful phase + current file in the log,
replacing the old black box. - scan.log skip-log — ignored / excluded files appear with a reason
instead of disappearing silently. - 4 style-detector FP-classes killed without weakening real-bug
detection:SCA017 DebugOutput(string-literal contents),SCA070 CommentedOutCode(inline-doc + doc-block start),SCA019 TodoComment
(detector-source mentions),SCA005 FormatMismatch(empty const
resolve). - FieldLeak-Detector —
FreeAndNil(Self.Field)withSelf.-
qualifier is now recognised (USepa.pas repro). - Two new configuration knobs —
[Detectors] MaxLineLength=N(default 120) and
[Detectors] MaxCaseBranches=N(default 10). - Anfänger-Build-Guide —
HowTo_Build{,_de}.mdwalks Delphi
newcomers from IDE installation to first IDE-plugin scan.
Earlier in the 0.9.8 cycle (the Phase 1 deliverables — still in this release)
13 commits since v0.9.7. Phase 1 of
Konzept_ScannerQualitaet.md is
complete (6/6 quick-wins); Phase 4 has begun with the A.3-Minimal
cross-unit visibility check. A multi-persona review (Architecture +
Security + Performance) hardened the code along the way.
Highlights
--time-detectorsMarkdown report — per-detector cumulative
wall-time + call count for data-driven optimisation.- Test-fixture auto-detection — findings from
uTest*.pas/
*Sample.pas/*Demo.pasand test/samples/demos/resources
directories are filtered out in thedefaultandselftest-quiet
profiles. Self-test report is now clean of intentional-fixture noise. - Unused-suppression tracking (SCA165) —
// noinspection Xmarkers
that never suppressed a finding are themselves flagged. Suppression
hygiene now visible. - Golden-corpus FP-regression suite — 5 historical FP reproducers
per Round-1..13 fix, PowerShell runner, CI-ready exit code. Every
future detector change is regression-guarded. - SARIF + Baseline contextHash — SHA256 over the ±3-line snippet
(whitespace-normalised) added to every finding. Baseline matches via
contextHash or legacy fingerprint — survives method renames and
small refactors. Old baselines remain valid (no migration needed). - Confidence audit (35 kinds →
fcMedium) — heuristic / metric /
style / DFM-schema / no-data-flow-security kinds tagged. With
--min-confidence highthe report shows only structural bugs without
losing detector coverage. - A.3-Minimal: SCA052 cross-unit reactivated —
gSymbolRefIndexis
now consulted forfkUnusedPublicMember. Methods called via
obj.Method(args)from another unit are no longer flagged as "dead
public API". - Security hardening —
noinspection Allexcludes critical kinds,
marker parsing is string-context-aware, test-fixture filter is
repo-root-anchored, baseline JSON has entry + length caps. - Performance —
gFileTextCacheis mtime-aware and lives across
the post-scan phase; saves ~191k redundant file reads + UTF-8
validations on a real-world scan.uVisibilityChecktree-walks cached
once per unit.
Phase 1 quick-wins (6/6)
C.4 Per-detector performance profile
--time-detectors writes a Markdown table with per-detector
cumulative wall-time, call count, average. Lets you pick optimisation
targets based on data, not gut feeling.
analyser.exe --path D:\repo --full --time-detectors > perf.mdA.2 Test-fixture auto-detection
TDetectorUtils.IsTestFixturePath returns true for paths matching:
- basenames:
uTest*.pas,*_Test.pas,*_Tests.pas,*Sample.pas,
*Demo.pas,*Sample_*.pas,*_Demo_*.pas,*Sample.dfm,
*Demo.dfm,MeineUnit.pas(and a few SCA-internal demo files) - repo-root-relative directory components:
test,tests,unittest,unittests,samples,demos,
resources
Auto-on for --profile default and selftest-quiet, off for strict.
Manual override via --hide-test-fixtures / --show-test-fixtures.
C.3 Unused-suppression tracking (SCA165)
For every // noinspection X marker we record Consumed = false
initially. When ApplyToFindings suppresses a finding via the marker,
Consumed := true. After the suppression pass, any marker still at
Consumed = false emits an fkUnusedSuppression finding on the marker
line, with a quick-fix hint to remove the marker.
C.1 Golden-corpus regression tests
tests/golden-corpus/fp-reproducers/ holds 5 historical false-positive
reproducer .pas snippets (one per Round-1..13 fix). expected.json
maps each file to a must_not_flag list of rule IDs. The PowerShell
runner tools/check-golden-corpus.ps1 scans the corpus and exits 0/1
based on whether any previously-fixed FP class fires again.
powershell -ExecutionPolicy Bypass -File tools\check-golden-corpus.ps1C.2 SARIF partialFingerprints.contextHash/v1
New unit uFindingFingerprint:
contextHash/v1 = "v1:" + SHA256( Normalize( ±3 lines around finding ) )
Normalize collapses whitespace runs to a single space, tabs → space,
empty lines dropped, CRLF / LF normalised. The hash is stable against
re-indent, trailing-whitespace cleanup and small line-drift refactors.
uBaseline.Apply matches contextHash or legacy fingerprint —
old baselines without contextHash remain valid (backward-compat),
new baselines survive small refactors.
A.1 Confidence-tagging audit
KindDefaultConfidence in uSCAConsts tags ~35 detector kinds as
fcMedium (pattern-match, metrics, style, DFM heuristics, security
without data-flow). All structural-bug detectors stay fcHigh.
TLeakFinding.SetKind reads from this map. Override pattern (e.g.
uCommandInjection := fcLow) is preserved via the new
SetKind(K, AConfidence) overload. Per-kind justifications in
docs/ConfidenceAudit.md.
Phase 4 partial — A.3 cross-unit visibility (begun)
Minimal step
uVisibilityCheck for fkUnusedPublicMember (SCA052) now consults
gSymbolRefIndex.HasExternalRefs before emitting. If any external unit
references the member via Obj.Member(args), no finding is emitted.
Audit (spot-check 18 known cross-unit methods)
- ✅ 8/18 (44 %) A.3 takes effect — pattern:
instance.Method(args) - ❌ 10/18 (56 %) A.3 inactive — 3 known index limitations:
- nkRef / value-use without parens (
F.SeverityText) - class-function-call
TClass.ClassMethod(args)— AST does not
classify asObj.Member - bare calls (
Fingerprint(F)) — deliberate index simplification
- nkRef / value-use without parens (
All three are documented in Konzept_ScannerQualitaet.md §A.3+ as the
prioritised follow-up sprint.
Library FP class (not addressable by the index)
Real-world scan shows the top SCA052 hot-spots are vendor libraries
(mormot.*, MVCFramework, LoggerPro) where the consumers live in
other repos that the index cannot see. Recommendation: path-override
default for vendor/ directories.
Multi-persona review
After Phase 1 + A.3-Minimal a three-persona review (Architecture +
Security + Performance) was run on the branch. 9 of 10 findings were
implemented in commit 120894a:
| # | Area | Fix |
|---|---|---|
| 1 | Perf | Baseline.Apply skips ContextHash for legacy baselines |
| 2 | Sec | noinspection All excludes security-critical kinds |
| 3 | Sec | ParseMarkerLine uses ScanCodeLine (string-context-aware) |
| 4 | Sec | IsTestFixturePath repo-root-anchored via BaseDir |
| 5 | Perf | gFileTextCache lives through post-scan + leak fix |
| 6 | Sec | Baseline JSON 1 M entry / 256 char cap |
| 8 | Perf | Suppression uses AcquireLines (cache hits) |
| 9 | Perf | uVisibilityCheck tree-walks cached per unit |
| 10 | API | SetKind(K, AConfidence) overload |
Finding #7 (DefaultConfidence into TFindingKindMeta record) deferred
as invasive (touches ~160 KIND_META entries) and would require an
explicit drift-test design.
TFileTextCache was subsequently made mtime-aware (commit 1e7e193) to
plug a stale-cache issue exposed by the regression suite after the
post-scan cache change.
Files added
StaticCodeAnalyserForm/sources/Infrastructure/uFindingFingerprint.pasStaticCodeAnalyserForm/tests/uTestFindingFingerprint.pastests/golden-corpus/README.mdtests/golden-corpus/fp-reproducers/fp01..fp05_*.pastests/golden-corpus/fp-reproducers/expected.jsontools/check-golden-corpus.ps1docs/ConfidenceAudit.mdKonzept_ScannerQualitaet.mddocs/releases/v0.9.8.md+v0.9.8_de.md(this file)
Files changed
uBaseline, uExportSARIF, uSuppression, uVisibilityCheck,
uDetectorUtils, uMethodd12, uSCAConsts, uConfidenceFilter
(tests), uConsoleRunner, uStaticAnalyzer2, uCommandInjection,
uCustomRuleDetector, uDivByZero, uLeakDetector2,
uFileTextCache, uFindingFilter, uMainForm, uIDEAnalyserForm.
Migration
No breaking changes for callers of the public CLI / SARIF / baseline.
- Existing baselines work as-is (matched via legacy fingerprint).
- New baselines additionally carry
contextHash. OldBaseline.Apply
versions ignore the unknown field. - Detectors that previously set
F.Confidence := xxxafterSetKind
should migrate toSetKind(K, AConfidence)to avoid the order-fragile
pattern. The old pattern still works.
Commit log (since v0.9.7)
1e7e193 fix(cache): mtime-aware cache-invalidation in TFileTextCache
2b723f7 fix(build): IsTestFixturePath impl signature
120894a fix(review): 9 review findings (Security + Perf + API)
e18323d refactor: Clean-code fixes (DRY, SRP, naming)
3054630 fix(visibility): A.3 OwnUnit path + roadmap update
0ab0bf4 feat(visibility): A.3-Minimal — gSymbolRefIndex for SCA052
a8c7c35 feat(confidence): A.1 audit — ~35 kinds as fcMedium
91ae2ec feat(baseline): C.2 SARIF contextHash + baseline match
7b957a8 test(corpus): C.1 Golden-corpus + runner
c0234d7 feat(suppression):C.3 Unused-suppression tracking (SCA165)
57a0b06 feat(filter): A.2 Test-fixture auto-detection
1b5a145 fix(perf): gDetectorTimings in interface section
79b4f56 feat(cli): --time-detectors flag