Skip to content

Commit 456c8c1

Browse files
committed
docs(roadmap): SonarDelphi migration plan (todo-sonardelphi-migration.md)
Internal working document for the next big initiative — porting SonarDelphi plugin coverage (144 rules in v1.18.3, IntegraDev fork) onto our SCA platform so SCA alone replaces SonarDelphi+SCA dual-install. Researched via two parallel agents: - Rule inventory and gap analysis: 22 SCA<->SonarDelphi overlaps, ~119 unique-to-SonarDelphi rules to port, ~37 unique-to-SCA differentiators (the 20 DFM rules + security/SQL family). - Technical migration strategy: ANTLR3 vs our recursive-descent parser, type-system gaps (we don't have cross-unit symbol resolution for types, only names), class-hierarchy index missing, per-rule effort estimate bucketed A-G by detection-approach category. Plan: - Phase 0 (1d): catalog-first placeholders for all 144 rule IDs - Phase 1 (2w): 50 lexical + single-AST detectors - Phase 2 (1w): Forbidden-tracker + Naming-conventions frameworks (25 rules via 2 frameworks) - Phase 3 (3w): 35 multi-node + cross-unit detectors - Phase 4 (2w): 12 inheritance/inline-decl rules + new uClassHierarchyIndex - Phase 5 (4w, optional): 17 type-flow rules + uTypeRegistry Total without Phase 5: 9 weeks for 122 ported + 22 existing = 144 (100%) plus 37 our-unique = 110% target reached. Shadow-run script and coverage-gap test scaffold described as cross-cutting tasks - implementation deferred to the actual migration sprints. Document follows the same convention as todo-sonar.md (checked-in working doc, not part of the release artifacts).
1 parent 362217c commit 456c8c1

1 file changed

Lines changed: 203 additions & 0 deletions

File tree

todo-sonardelphi-migration.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# TODO — SonarDelphi → SCA Detector Migration
2+
3+
**Goal: 110 % coverage of all SonarDelphi findings**, plus our existing
4+
SCA-unique detectors (DFM, security, SQL).
5+
6+
> **Recherche-Stand 2026-05-16**:
7+
> - SonarDelphi v1.18.3 (IntegraDev fork): **144 Rules** (25 BUG + 119 CODE_SMELL, 0 Vulnerability/Hotspot)
8+
> - SCA v0.9.1: **59 Rules**, davon ~22 mit SonarDelphi-Overlap, ~37 unique (20 DFM-Rules + SQL/Security/Format-Locale)
9+
> - Coverage-Gap zum 110 %-Ziel: **~119 Rules** zu portieren, **~25 Rules** schon vorhanden
10+
11+
---
12+
13+
## Context
14+
15+
Heute haben wir die SonarQube-Integration auf Production-Niveau gebracht
16+
(`v0.9.1`). SCA-Findings landen als External Issues neben dem SonarQube-
17+
Default-Profile "Sonar Way". Wenn ein User aber **auch** das SonarDelphi-
18+
Plugin installiert hat, sieht er TWO Detektor-Sets parallel — wir wollen,
19+
dass SCA allein ausreicht und das SonarDelphi-Plugin **überflüssig** wird.
20+
21+
"110 % Coverage" bedeutet:
22+
- Jeder SonarDelphi-Finding muss von SCA matched werden (sonst: regression
23+
beim Wechsel weg von SonarDelphi)
24+
- Unsere zusätzlichen Findings (DFM, SQL, Format-Locale) bleiben unsere
25+
Differentiation
26+
- **Shadow-Run**: SonarDelphi und SCA parallel über denselben Code-Korpus,
27+
diff der Findings nach `(file, line, rule)` — Ziel: SonarDelphi-only-
28+
Findings = 0
29+
30+
## Bestehende Infrastruktur (nicht neu bauen)
31+
32+
- AST: [`uAstNode.pas`](StaticCodeAnalyserForm/sources/Parsing/uAstNode.pas), [`uParser2.pas`](StaticCodeAnalyserForm/sources/Parsing/uParser2.pas) — Visitor-tauglich, 45 `TNodeKind`-Werte
33+
- Symbol-Reference-Index: [`uSymbolReferenceIndex.pas`](StaticCodeAnalyserForm/sources/Infrastructure/uSymbolReferenceIndex.pas) — Cross-Unit-Visibility (genutzt von `uVisibilityCheck`)
34+
- Catalog: [`rules/sca-rules.json`](rules/sca-rules.json) + `uRuleCatalog.pas`
35+
- KIND_META: [`uSCAConsts.pas`](StaticCodeAnalyserForm/sources/Common/uSCAConsts.pas) — Single-Source für Severity + FindingType + Name
36+
- Detector-Pattern: 53 Beispiele in [`StaticCodeAnalyserForm/sources/Detectors/`](StaticCodeAnalyserForm/sources/Detectors/)
37+
- Tests: DUnitX-Fixtures in `tests/uTest*.pas`
38+
39+
## Was SonarDelphi anders / besser macht
40+
41+
Nicht-portable Java-Stack:
42+
- **ANTLR3-AST** mit ~50 fein-granulierten Node-Klassen (`RoutineImplementationNode`, `AnonymousMethodNode`, `FieldDeclarationNode`, ...)
43+
- **Type-System mit Symbol-Resolution** über Unit-Imports (`getType().isUnresolved/isClass/isUnknown`)
44+
- **Inheritance-Index** ("ist Klasse direkt von TObject abgeleitet?")
45+
- **DelphiCheckContext + reportIssue()** Sonar-Framework-API
46+
47+
Bei uns fehlend:
48+
- Fein-granulierte AST-Subtypes (wir haben `nkMethod` als Einheits-Bucket)
49+
- Type-Registry mit Cross-Unit-Lookup
50+
- Class-Hierarchy-Index (parent_name_chain pro `class_name`)
51+
52+
## Phase Plan
53+
54+
### Phase 0 — Catalog-First (1 Tag) 🅐
55+
56+
**Ziel**: SonarDelphi-Push-Kompatibilität sofort, ohne Detection-Logik.
57+
58+
- [ ] Alle 144 SonarDelphi-Rule-Keys als `fk*`-Konstanten in [`uSCAConsts.pas`](StaticCodeAnalyserForm/sources/Common/uSCAConsts.pas) (TFindingKind enum + KIND_META) einpflegen
59+
- [ ] [`rules/sca-rules.json`](rules/sca-rules.json) auf 144 + 37 = **~181 Rules** erweitern (placeholders mit Beschreibung aus SonarDelphi-JSON)
60+
- [ ] MQR-Mapping pro neuer Rule (`cleanCodeAttribute` + `impacts`) — viel ist mechanisch übertragbar aus SonarDelphi-Severity
61+
- [ ] Drift-Tests `EveryFindingKindHasRichMetadata` + `EveryFindingKindHasMqrMapping` müssen weiterhin grün sein
62+
- [ ] **Acceptance**: `analyser.exe --sonar-export` schreibt 144+37 Rule-Entries; Sonar-Push akzeptiert die JSON
63+
64+
> **Wert**: dadurch verstummen die SonarDelphi-Findings im Dashboard nicht
65+
> mehr, weil unsere ID's dieselben sind — aber wir melden noch nichts.
66+
> User sieht "alles bekannt", nur wenige Findings (die SCA schon hat).
67+
68+
### Phase 1 — Lexical + Single-Node-AST (Kat A+B, ~50 Rules, 2 Wochen) 🅑
69+
70+
Triviale Detektoren — Pattern matched 1:1 unsere bestehenden.
71+
72+
**Kat A (Lexical, Regex/Substring, ~20 Rules)**:
73+
74+
`CommentRegularExpression`, `StringLiteralRegularExpression`, `TabulationCharacter`, `TrailingWhitespace`, `TooLongLine`, `CommentedOutCode`, `LowercaseKeyword`, `MissingSemicolon`, `SuperfluousSemicolon`, `RedundantParentheses`, `TrailingCommaArgumentList`, `DigitGrouping`, `DigitSeparator`, `NoSonar`, `MixedNames`, `InlineAssembly`, `LegacyInitializationSection`, `UnitLevelKeywordIndentation`, `VisibilityKeywordIndentation`, `PascalStyleResult`
75+
76+
**Vorlage**: [`uTodoComment.pas`](StaticCodeAnalyserForm/sources/Detectors/uTodoComment.pas), [`uHardcodedPath.pas`](StaticCodeAnalyserForm/sources/Detectors/uHardcodedPath.pas), [`uMagicNumbers.pas`](StaticCodeAnalyserForm/sources/Detectors/uMagicNumbers.pas).
77+
**Aufwand**: 1-2 h pro Rule.
78+
79+
**Kat B (AST single-node, ~30 Rules)**:
80+
81+
`EmptyArgumentList`, `EmptyBlock`, `EmptyFieldSection`, `EmptyFile`, `EmptyFinallyBlock`, `EmptyInterface`, `EmptyVisibilitySection`, `GotoStatement`, `GroupedFieldDeclaration`, `GroupedParameterDeclaration`, `GroupedVariableDeclaration`, `MemberDeclarationOrder`, `VisibilitySectionOrder`, `ConsecutiveConstSection`, `ConsecutiveTypeSection`, `ConsecutiveVarSection`, `ConsecutiveVisibilitySection`, `BeginEndRequired`, `CaseStatementSize`, `EmptyRoutine` (✅ haben wir), `RedundantBoolean`, `RedundantJump`, `ExplicitBitwiseNot`, `AssertMessage`, `PublicField`, `ProjectFileRoutine`, `ProjectFileVariable`, `ExplicitTObjectInheritance`, `EmptyInterface`, `ClassPerFile`
82+
83+
**Vorlage**: [`uEmptyMethod.pas`](StaticCodeAnalyserForm/sources/Detectors/uEmptyMethod.pas) (74 Zeilen), [`uDebugOutput.pas`](StaticCodeAnalyserForm/sources/Detectors/uDebugOutput.pas), [`uReversedForRange.pas`](StaticCodeAnalyserForm/sources/Detectors/uReversedForRange.pas).
84+
**Aufwand**: 2-4 h pro Rule.
85+
86+
**Acceptance Phase 1**:
87+
- [ ] DUnitX-Tests pro Rule mit SonarDelphi-Fixture-Files als Truth (aus `delphi-checks/src/test/resources/au/com/integradev/delphi/checks/<RuleName>/`)
88+
- [ ] Shadow-Run: 50 Rules sollten matching Findings produzieren
89+
90+
### Phase 2 — Configurable Forbidden + Naming-Conventions (Framework, ~25 Rules, 1 Woche) 🅒
91+
92+
**Diese 25 Rules teilen sich 2 Frameworks** — bauen wir die zwei, kriegen wir 25 Rules.
93+
94+
**Framework A — `TForbiddenChecker<T>`** (10 Rules):
95+
96+
`ForbiddenConstant`, `ForbiddenEnumValue`, `ForbiddenField`, `ForbiddenIdentifier`, `ForbiddenImportFilePattern`, `ForbiddenProperty`, `ForbiddenRoutine`, `ForbiddenType`, plus die zwei Regex-Tracker (`CommentRegularExpression`, `StringLiteralRegularExpression`).
97+
98+
Pattern: Config-Liste in `analyser.ini` `[ForbiddenIdentifiers]`, `[ForbiddenRoutines]`, etc. Vorlage existiert teilweise in [`uDfmForbiddenClass.pas`](StaticCodeAnalyserForm/sources/Detectors/uDfmForbiddenClass.pas).
99+
100+
**Framework B — `TNamingConventionChecker`** (16 Rules):
101+
102+
`AttributeName`, `ClassName`, `ConstantName`, `ConstructorName`, `DestructorName`, `EnumName`, `FieldName`, `HelperName`, `InheritedTypeName`, `InterfaceName`, `PointerName`, `RecordName`, `RoutineName`, `ShortIdentifier`, `UnitName`, `VariableName`
103+
104+
Pattern: ein Regex pro Naming-Kind, Default-Patterns aus SonarDelphi übernehmen (z.B. `T[A-Z][a-zA-Z0-9]*` für Class). Config-Override per `analyser.ini`.
105+
106+
**Acceptance Phase 2**: SonarDelphi-Default-Naming-Profile produziert identische Findings wie unser.
107+
108+
### Phase 3 — AST Multi-Node + Cross-Unit (Kat C+D, ~35 Rules, 3 Wochen) 🅓
109+
110+
**Kat C — AST multi-node matching, ~20 Rules**:
111+
112+
`FormatArgumentCount` (✅ ähnlich vorhanden in `uFormatMismatch`), `FormatArgumentType`, `FormatStringValid`, `IfThenShortCircuit`, `LoopExecutingAtMostOnce`, `RedundantAssignment`, `RedundantInherited`, `MissingRaise`, `RaisingRawException`, `CatchingRawException`, `ReRaiseException`, `SwallowedException` (✅ ähnlich `uCodeSmells2/EmptyExcept`), `NilComparison`, `InstanceInvokedConstructor`, `InterfaceGuid`, `ObjectType`, `ObjectPassedAsInterface`, `ExplicitDefaultPropertyReference`, `RedundantCast`, `IndexLastListElement`
113+
114+
**Vorlagen**: [`uFormatMismatch.pas`](StaticCodeAnalyserForm/sources/Detectors/uFormatMismatch.pas), [`uTautologicalExpr.pas`](StaticCodeAnalyserForm/sources/Detectors/uTautologicalExpr.pas), [`uMissingFinally.pas`](StaticCodeAnalyserForm/sources/Detectors/uMissingFinally.pas).
115+
116+
**Kat D — Cross-Unit (Symbol-Index nötig), ~15 Rules**:
117+
118+
`UnusedConstant`, `UnusedField`, `UnusedGlobalVariable`, `UnusedImport` (✅ `uUnusedUses`), `UnusedProperty`, `UnusedRoutine`, `UnusedType`, `UnusedLocalVariable` (✅ `uUnusedLocal`), `UnusedParameter` (✅), `TooManyDefaultParameters`, `TooManyVariables`, `TooManyNestedRoutines`, `FullyQualifiedImport`, `ImportSpecificity`, `TypeAlias`, `UnspecifiedReturnType`
119+
120+
**Vorlage**: [`uVisibilityCheck.pas`](StaticCodeAnalyserForm/sources/Detectors/uVisibilityCheck.pas) (nutzt `SymbolReferenceIndex`), [`uUnusedUses.pas`](StaticCodeAnalyserForm/sources/Detectors/uUnusedUses.pas).
121+
122+
**Acceptance Phase 3**: Cross-Unit-Coverage matched SonarDelphi's `UnusedX`-Familie.
123+
124+
### Phase 4 — Inline-Declarations + Inheritance (Kat F + Modern Delphi, ~12 Rules, 2 Wochen) 🅔
125+
126+
**Inline-Declarations (Delphi 10.3+, 5 Rules)**:
127+
128+
`InlineConstExplicitType`, `InlineVarExplicitType`, `InlineLoopVarExplicitType`, `InlineDeclarationCapturedByAnonymousMethod`, `AddressOfNestedRoutine`
129+
130+
**Inheritance-Index nötig** (7 Rules):
131+
132+
`ConstructorWithoutInherited`, `DestructorWithoutInherited`, `InheritedMethodWithNoCode`, `RedundantInherited`, `InheritedTypeName`, `ExplicitTObjectInheritance` (Bonus zu Phase 1), `EmptyInterface`
133+
134+
**Vorab-Investition**: Neue Unit `uClassHierarchyIndex.pas` analog [`uSymbolReferenceIndex.pas`](StaticCodeAnalyserForm/sources/Infrastructure/uSymbolReferenceIndex.pas) — baut `ClassName -> ParentChain` Repo-weit. ~2 Tage Vorlauf.
135+
136+
**Acceptance Phase 4**: `class(TFoo)` Vererbung wird über Unit-Grenzen resolved.
137+
138+
### Phase 5 — Type-Flow (Kat E, ~17 Rules, **OPTIONAL** 4 Wochen) 🅕
139+
140+
**Hard — braucht Type-Registry**:
141+
142+
`AddressOfCharacterData`, `CastAndFree`, `CharacterToCharacterPointerCast`, `FreeAndNilTObject`, `NonLinearCast`, `PlatformDependentCast`, `PlatformDependentTruncation`, `UnicodeToAnsiCast`, `MathFunctionSingleOverload`, `IterationPastHighBound`, `StringListDuplicates`, `DateFormatSettings`, `ImplicitDefaultEncoding`, `AssignedAndFree`, `VariableInitialization`, `CognitiveComplexityRoutine`, `RoutineResultAssigned`
143+
144+
**Vorab-Investition**: `uTypeRegistry.pas` mit Cross-Unit-Type-Resolution. ~1 Woche.
145+
146+
**Workaround ohne Type-Registry**: pattern-match auf bekannte Type-Names als Negativliste — liefert ~70 % Coverage. Akzeptabel für Phase 5 zum Start, später durch echte Type-Registry ersetzen.
147+
148+
**Diese Phase ist nicht teil des 110 %-Coverage-Ziels** wenn der Aufwand zu hoch ist — SonarDelphi selbst hat hier die höchste False-Positive-Rate. Wir können diese 17 Rules im Catalog als "deferred" markieren und kommunizieren.
149+
150+
## Cross-cutting Tasks
151+
152+
### Shadow-Run-Infrastruktur (1 Tag)
153+
154+
- [ ] Bash/PowerShell-Script `tools/shadow-diff-sonardelphi.ps1`:
155+
1. Test-Corpus (z.B. Embarcadero-Samples + unser Repo) gegen SonarDelphi pushen (Pfad: sonar-scanner mit SonarDelphi-Plugin)
156+
2. Gleichen Corpus gegen SCA pushen (`sonar-scan.ps1` + `sonar-upload.ps1`)
157+
3. Diff der Findings nach `(file, line, ruleId-equivalent)` via Mapping-Tabelle in `tools/sonardelphi-rule-mapping.json`
158+
4. Output: "SonarDelphi-only findings: X (REGRESSIONS)" / "SCA-only: Y (OK)" / "Matched: Z"
159+
- [ ] Mapping-Tabelle: SCA-RuleID ↔ SonarDelphi-RuleKey pflegen während Migration
160+
161+
### Drift-Test gegen 110 %-Ziel
162+
163+
- [ ] Neuer Test `uTestCoverageGap.pas`: lädt SonarDelphi-Rule-Inventar (statisches JSON checked-in als `tests/data/sonardelphi-rules-v1.18.3.json`), prüft pro Rule-Key ob ein SCA-Mapping-Eintrag existiert. Fehlende Mappings: Test rot.
164+
165+
### Test-Fixture-Import
166+
167+
- [ ] `tools/import-sonardelphi-fixtures.ps1` cloned SonarDelphi-Repo, kopiert `delphi-checks/src/test/resources/au/com/integradev/delphi/checks/<RuleName>/*.pas` als `tests/fixtures/sonardelphi/<RuleName>/`. Lizenz-Compliance: Header beachten, Quelle dokumentieren.
168+
169+
## Out of Scope
170+
171+
- **GUI** für SonarDelphi-Rule-Verwaltung — `analyser.ini` reicht für Phase 0-4
172+
- **Quality-Profile-Migration** — SonarQube-seitig, wir liefern nur Findings
173+
- **Auto-Fix-Suggestions** für SonarDelphi-Rules — separater Sprint
174+
- **Reverse-Migration** (SCA → SonarDelphi-Plugin) — explizit nicht das Ziel
175+
176+
## Empfehlung — Reihenfolge
177+
178+
| Phase | Items | Aufwand | ROI |
179+
|---|---|---|---|
180+
| **0** Catalog-First | 144 Rule-IDs + KIND_META + JSON | 1 d | Sofort: Sonar-Push akzeptiert alles |
181+
| **1** Lexical + Single-Node | 50 Rules (Kat A+B) | 2 w | Volumen-Gewinn, einfache Migration |
182+
| **2** Forbidden + Naming-Framework | 25 Rules über 2 Frameworks | 1 w | Hohe Rule-Anzahl pro Code-Aufwand |
183+
| **3** Multi-Node + Cross-Unit | 35 Rules (Kat C+D) | 3 w | Substanz, nutzt vorhandene Infrastruktur |
184+
| **4** Inline + Inheritance | 12 Rules + Hierarchy-Index | 2 w | Modern-Delphi-Coverage |
185+
| **5** Type-Flow (optional) | 17 Rules + Type-Registry | 4 w | High-FP-rate, separat planen |
186+
187+
**Total ohne Phase 5: ~9 Wochen für ~122 portierte Rules** plus die existierenden 22 Overlaps = **144 = 100 % SonarDelphi-Coverage**. Plus unsere 37 unique → **110 % erreicht**.
188+
189+
Mit Phase 5: ~13 Wochen für **161 Rules total** = **112 %**.
190+
191+
**Strikte Abhängigkeit**: Phase 0 vor allem anderen. Phase 4 braucht den Hierarchy-Index (Vorinvestition). Phase 5 braucht Type-Registry (separate Entscheidung).
192+
193+
---
194+
195+
## Sources
196+
197+
- [SonarDelphi v1.18.3 Repo](https://github.com/integrated-application-development/sonar-delphi/tree/v1.18.3) — 144 Rules, ANTLR3-AST
198+
- Rule JSONs: `delphi-checks/src/main/resources/org/sonar/l10n/delphi/rules/community-delphi/*.json`
199+
- Check-Implementations: `delphi-checks/src/main/java/au/com/integradev/delphi/checks/*Check.java`
200+
- Test-Fixtures: `delphi-checks/src/test/resources/au/com/integradev/delphi/checks/<RuleName>/`
201+
- Unsere Rule-Liste: [`rules/sca-rules.json`](rules/sca-rules.json) (59 Regeln, MQR-tauglich)
202+
- Bestehender Sonar-Workflow: [`sonarHowto.md`](sonarHowto.md), [`StaticCodeAnalyserForm/scripts/`](StaticCodeAnalyserForm/scripts/)
203+
- Verwandter TODO: [`todo-sonar.md`](todo-sonar.md) (Sonar-Integration — abgeschlossen mit v0.9.1)

0 commit comments

Comments
 (0)