Skip to content

Commit 43fd0e6

Browse files
committed
fix(detector): uExceptionTooGeneral skippt legit Top-Level-Handler (SCA078 ~40 FPs)
CLI-Runner und Worker-Threads brauchen einen breiten Top-Level-Catch um Crashes in saubere Error-Messages zu uebersetzen statt das Programm hart abstuerzen zu lassen. Self-Test fand davon 40+ in uConsoleRunner, uStaticAnalyzer2, uIDEAnalyseRunner. Pattern (legitim): except on E: Exception do begin WriteLn(ErrOutput, 'Fatal: ', E.Message); Exit(Integer(cecToolError)); end; end; Neue Heuristik IsLegitTopLevelHandler walked den nkOnHandler-Subtree und prueft auf gleichzeitige Praesenz von: * LOG-Pattern (WriteLn / Write / Log* / OutputDebug* / ShowMessage) * LEAVE-Pattern (Halt / Exit / raise) Wenn beide -> kein Swallow, sondern saubere Crash-Translation, kein Finding. Schmal genug um die echten 'except on E: Exception do log(E)' ohne Exit weiterhin zu treffen (klassisches Swallow).
1 parent afd43b6 commit 43fd0e6

1 file changed

Lines changed: 72 additions & 0 deletions

File tree

StaticCodeAnalyserForm/sources/Detectors/uExceptionTooGeneral.pas

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@
3838
// (fkEmptyExcept fuer leere Variante) deckt das ab.
3939
// * `on E: EAbort do ... raise;` mit re-raise - dort wuerde Filter
4040
// greifen, aber TypeRef='EAbort' nicht 'Exception'.
41+
// * Legit Top-Level-Handler die LOGGEN und beenden:
42+
// on E: Exception do
43+
// begin
44+
// WriteLn(ErrOutput, 'Fatal: ', E.Message);
45+
// Exit(Integer(cecToolError));
46+
// end;
47+
// Diese Pattern faengt zwar 'Exception' breit, ist aber bewusst die
48+
// defensive Schutzschicht am Top-Level (CLI-Runner, Worker-Threads).
49+
// Heuristik: Body enthaelt einen Log-Call (WriteLn/Write/Log/Output*)
50+
// UND einen Exit/Halt - dann ist es kein Swallow, sondern saubere
51+
// Crash-Translation.
4152
//
4253
// Sonar-Pendant: ExceptionTooGeneral / "java:S2221" (Java)
4354
// S110 Pattern bezogen auf Delphi-Hierarchie.
@@ -59,6 +70,66 @@ TExceptionTooGeneralDetector = class
5970

6071
implementation
6172

73+
function IsLegitTopLevelHandler(OnNode: TAstNode): Boolean;
74+
// True wenn der Handler-Body sowohl LOGGT (WriteLn/Write/Log*/OutputDebug*)
75+
// als auch BEENDET/RAISE'T (Exit/Halt/raise). Dann ist es kein blindes
76+
// Swallow, sondern saubere Crash-Translation am Top-Level.
77+
//
78+
// Heuristik scannt nur die DIREKTEN Calls im Subtree des OnHandlers - kein
79+
// Daten-Fluss, kein Symbol-Lookup. Schmal genug um nur die klare 'log+exit'
80+
// und 'log+raise' Pattern zu treffen.
81+
var
82+
Stack : TList<TAstNode>;
83+
Cur : TAstNode;
84+
i : Integer;
85+
NameLow : string;
86+
HasLog : Boolean;
87+
HasLeave : Boolean;
88+
begin
89+
Result := False;
90+
HasLog := False;
91+
HasLeave := False;
92+
Stack := TList<TAstNode>.Create;
93+
try
94+
Stack.Add(OnNode);
95+
while Stack.Count > 0 do
96+
begin
97+
Cur := Stack[Stack.Count - 1];
98+
Stack.Delete(Stack.Count - 1);
99+
100+
// raise; (bare re-raise) ist eine Form von Leave - Exception
101+
// propagiert nach oben.
102+
if Cur.Kind = nkRaise then HasLeave := True;
103+
if Cur.Kind = nkExit then HasLeave := True;
104+
105+
if Cur.Kind = nkCall then
106+
begin
107+
NameLow := LowerCase(Cur.Name);
108+
// Log-Pattern: WriteLn/Write/Log*/OutputDebugString/ShowMessage
109+
if NameLow.StartsWith('writeln(') or
110+
NameLow.StartsWith('write(') or
111+
NameLow.StartsWith('outputdebug') or
112+
NameLow.StartsWith('log') or
113+
NameLow.StartsWith('showmessage(') or
114+
NameLow.StartsWith('savetofile(') then
115+
HasLog := True;
116+
// Leave-Pattern: Halt(...) / Exit(...)
117+
if NameLow.StartsWith('halt(') or
118+
NameLow.StartsWith('halt;') or
119+
NameLow.StartsWith('exit(') or
120+
NameLow.StartsWith('exit;') then
121+
HasLeave := True;
122+
end;
123+
if HasLog and HasLeave then Exit(True);
124+
125+
for i := 0 to Cur.Children.Count - 1 do
126+
Stack.Add(Cur.Children[i]);
127+
end;
128+
finally
129+
Stack.Free;
130+
end;
131+
end;
132+
62133
class procedure TExceptionTooGeneralDetector.AnalyzeMethod(MethodNode: TAstNode;
63134
const FileName: string; Results: TObjectList<TLeakFinding>);
64135
var
@@ -71,6 +142,7 @@ class procedure TExceptionTooGeneralDetector.AnalyzeMethod(MethodNode: TAstNode;
71142
for N in Handlers do
72143
begin
73144
if not SameText(N.TypeRef, 'Exception') then Continue;
145+
if IsLegitTopLevelHandler(N) then Continue;
74146

75147
F := TLeakFinding.Create;
76148
F.FileName := FileName;

0 commit comments

Comments
 (0)