Static code analysis for Delphi with TMS FixInsight v2021.10
We are pleased to announce the immediate availability of the new TMS FixInsight version v2021.10!
Highlights of this new release are:
- Delphi 11
support - Better type resolving
- Improved & more robust code parser
- 5 New code analysis rules added
The first three highlights are quite obvious, but let’s have a deeper look at the 5 new code analysis rules that have been added and that will enable you to detect possible code issues faster.
W537 Format parameter type mismatch
The Delphi compiler is not able to check data types used in Format() function. The example below will compile, but will never work properly. FixInsight is able to catch that.
var StringTypeParameter: string; begin Result := Format('String type parameter value = %d', [StringTypeParameter]); end;
W539 Interface method call passing the same interface reference as out parameter
In the example below access violation will be raised because
Delphi compiler cleans up Node variable before the actual Node.GetParent()
function call.
type INode = interface ['{39EE5CA9-C4C1-46E1-B326-BAA2D004A5CD}'] function GetParent(out Parent: INode): Boolean; end; TNode = class(TInterfacedObject, INode) strict private // INode. function GetParent(out Parent: INode): Boolean; end; { TNode } function TNode.GetParent(out Parent: INode): Boolean; begin Parent := TNode.Create; Result := True; end; procedure InterfaceErrorSample; var Node: INode; begin Node := TNode.Create; // Before calling Node GetParent, the compiler generates code that clears the Node variable // because the GetParent parameter is declared with the out parameters. // Thus calling Node.GetParent will result in AV. while Node.GetParent(Node) do // Getting root node. ; // Do somthing with root node. end;
W540 String variable used twice in a call both as an output and an input parameter
In the example below Str parameter in
IsStringSliceNotEmpty() function will always be an empty string if you pass the
same variable twice. Because the variable will be cleaned up by Delphi
compiler.
function IsStringSliceNotEmpty(const Str: string; out Slice: string; Index, Count: Integer): Boolean;
begin
Slice := Copy(Str, Index, Count);
Result := Slice <> '';
end;
procedure StringErrorSample;
var
Str: string;
begin
Str := 'qwerty';
if IsStringSliceNotEmpty(Str, Str, 1, Length(Str)) and (Str[Length(Str)] = 'y') then
ShowMessage(Str); // ShowMessage never run because Str was cleared before the SliceString call.
end;
W541 Type-casting from Pointer to Integer type (or vice
versa)
Type casting from Pointer to Integer and Integer to Pointer
is often used in legacy code. But it is not safe on some platforms, because
types size are differ. For instance, Pointer is 8 byte on x64, and the code
below will lead to data loss.
procedure TypecastSample(A: Pointer); var B: Integer; begin B := Integer(A); //... end;
W542 Direct floating-point comparison
There are several not obvious problems with comparing floating-point
numbers. They are perfectly explained in DocWiki https://docwiki.embarcadero.com/RADStudio/Sydney/en/Floating-Point_Comparison_Routines
In short, you should never use equal sign to compare two
floating-point numbers, but rather use special comparison routines.
var X:Single; Y:Double; begin X:=0.1; Y:=0.1; if X=Y then Writeln('Equal') else Writeln('Not equal'); ReadLn; end.
The program outputs:
Not equal
Get the update & increase the quality of your code!
If you have an active license for TMS FixInsight, the update is available for you free. Users with an expired license can purchase a renewal at discount price.
If you haven’t used TMS FixInsight before, download our trial version and see how it will offer tons of suggestions to improve your code.