David Heffernan 2402 Posted Thursday at 02:39 PM I've recently come across a defect in my code that looks like this: msg := Format('%s' + foo, [bar]); If foo contains any format placeholders, e.g. %s, %20, etc. then this will result in an exception being raised. It's a stupid mistake, but now I want to check my entire codebase to see we've done it elsewhere. Ideally I'd like a static tool that detects any call to Format or common equivalent like Exception.CreateFmt for which the format string is not a literal. I don't think FixInsight has such a warning. Does anybody know of such a tool? Share this post Link to post
Rollo62 562 Posted Thursday at 03:18 PM (edited) That would mean, that this tool "compiles" the code and is able to calculate the contents of the variable "foo". This can be from millions of different complex calculations and sources and even user input. I doubt that such tool will exists, but perhaps a Pascal parser together with a Pascal scripter may be able to simulate the codeflow and catch this. Maybe its easier, if you replace/mock the Format() and try to catch or log this error before it is raised. Edited Thursday at 03:19 PM by Rollo62 Share this post Link to post
David Heffernan 2402 Posted Thursday at 03:51 PM 25 minutes ago, Rollo62 said: That would mean, that this tool "compiles" the code and is able to calculate the contents of the variable "foo". This can be from millions of different complex calculations and sources and even user input. I doubt that such tool will exists, but perhaps a Pascal parser together with a Pascal scripter may be able to simulate the codeflow and catch this. Maybe its easier, if you replace/mock the Format() and try to catch or log this error before it is raised. No, I am just looking for a tool that detects non literal args by static analysis, which is what I wrote Share this post Link to post
Kryvich 174 Posted Thursday at 07:13 PM (edited) Copilot. Quote Write a console utility in Delphi that will scan the specified folder with subfolders, open files with the PAS extension, and select all strings starting with "Format(" and ending with "])". Among the strings found, you need to select those that contain the "+" character before the "," character, and save these strings in the text file "format.log". The result: program FormatStringExtractor; uses SysUtils, Classes; function ContainsPlusBeforeComma(const S: string): Boolean; var PlusPos, CommaPos: Integer; begin PlusPos := Pos('+', S); CommaPos := Pos(',', S); Result := (PlusPos > 0) and (CommaPos > PlusPos); end; procedure ProcessFile(const FileName: string; LogFile: TStrings); var Lines: TStringList; Line, SubStr: string; StartPos, EndPos: Integer; begin Lines := TStringList.Create; try Lines.LoadFromFile(FileName); for Line in Lines do begin StartPos := Pos('Format(', Line); while StartPos > 0 do begin EndPos := PosEx('])', Line, StartPos); if EndPos > 0 then begin SubStr := Copy(Line, StartPos, EndPos - StartPos + 2); if ContainsPlusBeforeComma(SubStr) then LogFile.Add(SubStr); // Move the start position forward to find the next occurrence StartPos := PosEx('Format(', Line, EndPos + 2); end else Break; end; end; finally Lines.Free; end; end; procedure ProcessFolder(const Folder: string; LogFile: TStrings); var SearchRec: TSearchRec; FullPath: string; begin if FindFirst(Folder + '\*.*', faAnyFile, SearchRec) = 0 then begin repeat FullPath := Folder + '\' + SearchRec.Name; if (SearchRec.Attr and faDirectory) = faDirectory then begin if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then ProcessFolder(FullPath, LogFile); end else if ExtractFileExt(SearchRec.Name).ToLower = '.pas' then begin ProcessFile(FullPath, LogFile); end; until FindNext(SearchRec) <> 0; FindClose(SearchRec); end; end; var Folder: string; LogFile: TStringList; begin Write('Enter folder path to scan: '); ReadLn(Folder); LogFile := TStringList.Create; try ProcessFolder(Folder, LogFile); LogFile.SaveToFile('format.log'); WriteLn('Strings saved to format.log'); finally LogFile.Free; end; end. Edited Thursday at 07:20 PM by Kryvich Share this post Link to post
David Heffernan 2402 Posted Thursday at 07:36 PM I know how to code a solution. And I'd use a proper parser to do it. But I'm looking for a tool. Share this post Link to post
David Heffernan 2402 Posted Thursday at 07:38 PM 17 minutes ago, Kryvich said: The result: Doesn't look like it handles code laid out over multiple lines. Share this post Link to post
Kryvich 174 Posted Thursday at 08:57 PM 1 hour ago, David Heffernan said: I know how to code a solution. And I'd use a proper parser to do it. But I'm looking for a tool. Definitely. But time is money. If Copilot can do it for you - why not? 1 hour ago, David Heffernan said: Doesn't look like it handles code laid out over multiple lines. for Line in Lines do --> Line := Lines.Text; Share this post Link to post
Remy Lebeau 1534 Posted Thursday at 09:16 PM 5 hours ago, Rollo62 said: That would mean, that this tool "compiles" the code and is able to calculate the contents of the variable "foo". Or, more simply, just detect a function call involving an "array of const" argument preceded by a string argument, and then warn if the string argument is not a literal. 1 Share this post Link to post