Static code analysis for Delphi with TMS FixInsight v2021.10

TMS Software Delphi  Components

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.