Skip to content

Commit 78825a0

Browse files
committed
feat(sca166): Managed-Alias - Vendor-Praefix-Interfaces (IwbX) mit TypeIndex-Negativ-Gate
IsManagedType erkennt Hersteller-Praefix-Interfaces: 'I' + >=1 Klein- buchstabe + Grossbuchstabe (IwbMainRecord ja; Integer/Int64 nein). Drop-Sampling-Erkenntnis 2026-07-24: die TES5Edit-Iwb-FPs ueberlebten nur zufaellig via CFG-Zyklus-Regel statt prinzipiell als managed. NEGATIV-GATE ueber CtxTypeIndex (nil-sicher): kennt der Index den Namen als Klasse/Record/Enum, wird die Konvention geblockt - Korpus-Beleg fuer die Notwendigkeit: IntUserFieldVTable/InstException = class, ImageCodecInfo u.a. = record (I+klein+Gross trifft reale Werttypen). tkiAlias bewusst NICHT als managed gewertet (TMyInt = Integer waere sonst maskiert); die TFileName-Alias-Klasse braucht echte Alias-Ziel- Aufloesung im Index (Backlog). REICHWEITEN-GRENZE (dokumentiert): Interfaces liegen im Index als tkiClass (Parser fuehrt sie als nkClass) - liegt die Interface-Decl im Scan-Umfang, blockt das Gate auch echte Interfaces. Wirkung daher nur fuer Fremd-Interfaces ohne mitgescannte Decl = der typische Kunden- Scan; im Bibliotheks-Selbst-Scan-Korpus byte-identisch (A/B after77<-> after78 BYTE-IDENTISCH, Gate bestanden). Volle Wirkung = tkiInterface- Unterscheidung via Parser-Marker (eigenes Inkrement, Backlog). Tests: VendorPrefixInterfaceLocal_NoFinding (FindingsOfFile, Index leer -> Konvention greift) + VendorPrefixButClass_StillFlagged (Pipeline, Index kennt die in-unit-Klasse -> Gate blockt, Fund bleibt).
1 parent 380011a commit 78825a0

2 files changed

Lines changed: 99 additions & 2 deletions

File tree

SCA.Engine/sources/Detectors/uUninitVar.pas

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,8 @@ function CountWholeWordOccurrences(const NeedleLow, HaystackLow: string;
329329
end;
330330
end;
331331

332-
function IsManagedType(const TypeRef: string): Boolean;
332+
function IsManagedType(const TypeRef: string;
333+
AContext: TAnalyzeContext): Boolean;
333334
var
334335
T, Orig : string;
335336
Prefix : string;
@@ -346,6 +347,40 @@ function IsManagedType(const TypeRef: string): Boolean;
346347
// (I + Kleinbuchstabe) matchen NICHT.
347348
if (Length(Orig) >= 2) and (Orig[1] = 'I') and CharInSet(Orig[2], ['A'..'Z']) then
348349
Exit(True);
350+
// SCA166-Managed-Alias (Drop-Sampling-Erkenntnis 2026-07-24): Hersteller-
351+
// Praefix-Interfaces wie 'IwbMainRecord' (TES5Edit: 'I' + Projektkuerzel
352+
// in Kleinbuchstaben + CamelCase-Name) fielen durch die strikte
353+
// I-Grossbuchstaben-Konvention - ihre FPs ueberlebten nur zufaellig via
354+
// Zyklus-Regel des CFG-Postfilters. Muster: 'I' + >=1 Kleinbuchstabe +
355+
// Grossbuchstabe ('IwbX' ja; 'Integer'/'Int64' nein - nach deren
356+
// Kleinlauf folgt kein Grossbuchstabe). NEGATIV-GATE ueber den Cross-
357+
// Unit-TypeIndex: kennt er den Namen als Klasse/Record/Enum, ist es
358+
// KEIN Interface -> nicht managed (eine uninitialisiert gelesene
359+
// KLASSEN-Referenz ist ein echter Bug und bleibt Fund). Korpus-Beleg
360+
// fuer die Strenge: IntUserFieldVTable/InstException = class,
361+
// ImageCodecInfo & Co. = record - das Muster trifft reale Werttypen.
362+
// REICHWEITEN-GRENZE (byte-identisch im Selbst-Scan-Korpus, A/B
363+
// after77/78): der Parser legt Interfaces als nkClass ab, der Index
364+
// fuehrt sie als tkiClass - liegt die Interface-DECL im Scan-Umfang,
365+
// blockt das Gate auch echte Interfaces. Die Konvention wirkt damit
366+
// fuer FREMD-Interfaces ohne mitgescannte Decl (der typische Kunden-
367+
// Scan). Volle Wirkung braeuchte tkiInterface im Index (Parser-
368+
// Marker an nkClass = eigenes risikobehaftetes Inkrement, Backlog).
369+
if (Length(Orig) >= 3) and (Orig[1] = 'I') and CharInSet(Orig[2], ['a'..'z']) then
370+
begin
371+
var i := 2;
372+
while (i <= Length(Orig)) and CharInSet(Orig[i], ['a'..'z']) do
373+
Inc(i);
374+
if (i <= Length(Orig)) and CharInSet(Orig[i], ['A'..'Z']) then
375+
begin
376+
// CtxTypeIndex kann nil sein (Tests/Single-File ohne Kontext) -
377+
// dann greift die Konvention ohne Gate (gleiche Vertrauensstufe
378+
// wie die bestehende I-Grossbuchstaben-Regel).
379+
var TI := CtxTypeIndex(AContext);
380+
if (TI = nil) or (TI.TypeKindOf(T) in [tkiUnknown, tkiAlias]) then
381+
Exit(True);
382+
end;
383+
end;
349384
end;
350385

351386
function IsNoInitRecordType(const TypeRef: string): Boolean;
@@ -1982,7 +2017,7 @@ class procedure TUninitVarDetector.AnalyzeMethod(MethodNode: TAstNode;
19822017
VarRec.FirstWriteLine := 0;
19832018
VarRec.FirstReadLine := 0;
19842019
VarRec.RefCount := 0;
1985-
VarRec.IsManaged := IsManagedType(LV.TypeRef);
2020+
VarRec.IsManaged := IsManagedType(LV.TypeRef, AContext);
19862021
// Duplikate (same name in nested-scope - selten, defensive skip)
19872022
if VarMap.ContainsKey(VarRec.NameLow) then Continue;
19882023
VarMap.Add(VarRec.NameLow, VarList.Count);

StaticCodeAnalyserForm/tests/uTestUninitVar.pas

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ TTestUninitVar = class
126126
[Test] procedure CfgUnguardedLoopConditionRead_StillFlagged; // bson-Muster: Regel B braucht Guard
127127
[Test] procedure DeclInitializedLocal_NoFinding; // FPC 'V: Integer = 1' = Write
128128
[Test] procedure DeclCommentEquals_StillFlagged; // '=' im Kommentar != Initializer
129+
// Managed-Alias 2026-07-24: Hersteller-Praefix-Interfaces (IwbX)
130+
[Test] procedure VendorPrefixInterfaceLocal_NoFinding;
131+
[Test] procedure VendorPrefixButClass_StillFlagged;
129132
end;
130133

131134
implementation
@@ -2179,6 +2182,65 @@ procedure TTestUninitVar.DeclCommentEquals_StillFlagged;
21792182
finally F.Free; end;
21802183
end;
21812184

2185+
procedure TTestUninitVar.VendorPrefixInterfaceLocal_NoFinding;
2186+
// Managed-Alias: 'IwbThing' (I + Kleinpraefix + CamelCase) ist nach Delphi-
2187+
// Konvention ein Interface -> compiler-nil-initialisiert -> nie ein echter
2188+
// uninit-Read. Vor dem Fix fiel das Muster durch die I-GROSSbuchstaben-
2189+
// Konvention (Drop-Sampling TES5Edit: IwbGroupRecord & Co.).
2190+
const
2191+
SRC =
2192+
'unit u;'#13#10 +
2193+
'interface'#13#10 +
2194+
'implementation'#13#10 +
2195+
'procedure P;'#13#10 +
2196+
'var G: IwbThing; X: Integer;'#13#10 +
2197+
'begin'#13#10 +
2198+
' X := G.Count;'#13#10 +
2199+
' WriteLn(X);'#13#10 +
2200+
'end;'#13#10 +
2201+
'end.'#13#10;
2202+
var
2203+
F : TObjectList<TLeakFinding>;
2204+
begin
2205+
RunOn(SRC, F);
2206+
try
2207+
Assert.AreEqual<Integer>(0, CountKind(F, fkUninitVar),
2208+
'Hersteller-Praefix-Interface ist managed -> kein uninit-Fund');
2209+
finally F.Free; end;
2210+
end;
2211+
2212+
procedure TTestUninitVar.VendorPrefixButClass_StillFlagged;
2213+
// TP-Gegenprobe (Negativ-Gate): der Name sieht aus wie ein Vendor-
2214+
// Interface, ist laut TypeIndex aber eine KLASSE - uninitialisiert
2215+
// gelesene Klassen-Referenz bleibt ein echter Fund. Voller Pipeline-
2216+
// Weg, damit der Cross-Unit-TypeIndex die in-unit-Klasse kennt.
2217+
const
2218+
SRC =
2219+
'unit u;'#13#10 +
2220+
'interface'#13#10 +
2221+
'type'#13#10 +
2222+
' IwbFake = class'#13#10 +
2223+
' public'#13#10 +
2224+
' Count: Integer;'#13#10 +
2225+
' end;'#13#10 +
2226+
'implementation'#13#10 +
2227+
'procedure P;'#13#10 +
2228+
'var G: IwbFake; X: Integer;'#13#10 +
2229+
'begin'#13#10 +
2230+
' X := G.Count;'#13#10 +
2231+
' WriteLn(X);'#13#10 +
2232+
'end;'#13#10 +
2233+
'end.'#13#10;
2234+
var
2235+
F : TObjectList<TLeakFinding>;
2236+
begin
2237+
F := TFindingHelper.FindingsViaPipeline(SRC);
2238+
try
2239+
Assert.IsTrue(CountKind(F, fkUninitVar) >= 1,
2240+
'TypeIndex kennt IwbFake als Klasse -> Konvention geblockt, Fund bleibt');
2241+
finally F.Free; end;
2242+
end;
2243+
21822244
initialization
21832245
TDUnitX.RegisterTestFixture(TTestUninitVar);
21842246

0 commit comments

Comments
 (0)