В этом посте я продолжу анализ библиотек поставляемых с Delphi. В предыдущих сериях я рассмотрел Delphi VCL и RTL. Как обычно я постараюсь найти все самое интересное и пропущу разнообразные мелочи, иначе этот пост получится слишком длинным. Все примеры кода в этом посте касаются Delphi XE8 (версия 22.0.19027.8951).

W517 Variable hides a class field, method or property

procedure TLight.ReadDiffuse(Reader: TReader);
var
  Color: Integer;
begin
  IdentToAlphaColor(Reader.ReadIdent, Color);
  {$R-}
  Color := TAlphaColor(Color);
  {$R+}
end;

И то же самое в другом модуле.

procedure TStrokeCube.ReadDiffuse(Reader: TReader);
var
  Color: Integer;
begin
  IdentToAlphaColor(Reader.ReadIdent, Color);
  {$R-}
  Color := TAlphaColor(Color);
  {$R+}
end;

Это одно из тех многочисленных предупреждений «переменная скрывает поле класса», о которых я говорил в предыдущем посте. В этом случае мы действительно видим баг. Классы TLight и TStrokeCube имеют свойство Color (типа TAlphaColor, конечно). Так что я предполагаю, что на самом деле имелось ввиду что-то вроде «Self.Color := TAlphaColor(Color)», но лучше просто переименовать переменную. Использовать одинаковые имена для переменных и полей класса — это очень плохой стиль. И Embarcadero, к сожалению, им злоупотребляет. Часто это совершенно не вредит, но иногда ведет к трудноотлавливаемым багам вроде этого. Просто посмотрите на этот код, запомните и никогда так не делайте. Никогда.

W511 Object created in TRY block

begin
  if ([csDesigning, csDestroying, csLoading, csUpdating] * ComponentState <> []) or
     (FUpdating > 0) then Exit;
  { Update objects in form }
  try
    Comparer := TComparerTFmxObject.Create;
    ClientList := TTObjInfoList.Create(Comparer);
    Bucket := TDictionary<TObject, TObject>.Create;
    for InitiatedCount := 0 to 7 do
    begin
      if CollectActionClients(ClientList) = 0 then
        Break;
      ClientList.Sort;
      for I := 0 to ClientList.Count - 1 do
        ClientList[I].ActionClient.InitiateAction;
      ClientList.Clear;
    end;
  finally
    FreeAndNil(ClientList);
    FreeAndNil(Bucket);
  end;
end;

Приложение может вызвать исключение до того как ссылка на экземпляр класса будет присвоена переменным ClientList и/или Bucket. Это значит, что в блоке finally может быть попытка освободить объекты, которые не создавались.

W523 Interface declared without a GUID

  IModelImporter = interface
    function GetDescription: string;
    function GetExt: string;

    function LoadFromFile(const AFileName: string; out AMesh: TMeshDynArray;
      const AOwner: TComponent): Boolean;
  end;
  IFMXUISwitch = interface(UISwitch)
    { Touches }
    procedure touchesBegan(touches: NSSet; withEvent: UIEvent); cdecl;
    procedure touchesCancelled(touches: NSSet; withEvent: UIEvent); cdecl;
    procedure touchesEnded(touches: NSSet; withEvent: UIEvent); cdecl;
    procedure touchesMoved(touches: NSSet; withEvent: UIEvent); cdecl;
    procedure ValueChanged; cdecl;
  end;

Не баг на самом деле, но так как интерфейсам не присвоен GUID, то они не могут быть использованы с функцией Supports или оператором As. Может быть стоит добавить GUID, почему нет?

W510 Values on both sides of the operator are equal

function SamePosition(const APosition1, APosition2: TPosition): Boolean; overload;
begin
  Result := (APosition1.X = APosition2.X) and (APosition1.Y = APosition1.Y);
end;

Предположу, что имелось ввиду «APosition1.Y = APosition2.Y».

W508 Variable is assigned twice successively

  FPixelShader := TShaderManager.RegisterShaderFromData('gouraud.fps', TContextShaderKind.PixelShader, '', [
    TContextShaderSource.Create(TContextShaderArch.DX9, [
      $00, $02, $FF, $FF, $FE, $FF, $32, $00, $43, $54, $41, $42, $1C, $00, $00, $00, $9F, $00, $00, $00, $00, $02, $FF, $FF, $03, $00, $00, $00, $1C, $00, $00, $00, $00, $01, $00, $20, $98, $00, $00, $00,
// skipped
  FPixelShader := TShaderManager.RegisterShaderFromData('gouraud.fps', TContextShaderKind.PixelShader, '', [
    TContextShaderSource.Create(TContextShaderArch.DX9, [
      $00, $02, $FF, $FF, $FE, $FF, $32, $00, $43, $54, $41, $42, $1C, $00, $00, $00, $9F, $00, $00, $00, $00, $02, $FF, $FF, $03, $00, $00, $00, $1C, $00, $00, $00, $00, $01, $00, $20, $98, $00, $00, $00,
// skipped

Два одинаковых присвоения одной переменной подряд. Вероятно не баг, просто неаккуратная копипаста.

W503 Assignment right hand side is equal to its left hand side

  if X1 > X2 then
    X1 := X1;
  if Y1 > Y2 then
    Y1 := Y2;

Предположу, что имелось ввиду «X1 := X2».

      if (Self.Form <> nil) and (Self.Form.Handle <> nil) then
        Self.Form := Self.Form;

Не знаю что здесь должно было быть (Self.Form здесь — это поле record’а).

И еще одно похожее место.

      if FNew.FFrequency <> 0 then
        FNew.FValue := Round(FNew.FValue / FNew.FFrequency) * FNew.FFrequency
      else
        FNew.FValue := FNew.FValue;

W510 Values on both sides of the operator are equal

            if RegionSize = RegionSize then
            begin
              SetLength(UpdateRects, RegionData.rdh.nCount);
              for i := 0 to RegionData.rdh.nCount - 1 do
              begin
                R := PRgnRects(@RegionData.buffer[0])[i];
                UpdateRects[i] := RectF(R.Left, R.Top, R.Right, R.Bottom);
              end;
            end;

Что здесь имелось ввиду?

W513 Format parameter count mismatch

function TCustomValueRange.GetNamePath: string;
begin
  Result := Format( 'Value: %0:*.*f (%1:*.*f..%2:*.*f)', [Value, Min, Max]);
end;

Строка форматирования неправильная. При выполнении этого кода будет сообщение об ошибке.

W505 Empty THEN block

      if FoundValue.Count > 1 then
      else if FoundValue.Count > 0 then
        PropValues[Name] := FoundValue[0];

Этот код очень странно выглядит. Вероятно его можно заменить на что-то такое: «If FoundValue.Count = 1 then PropValues[Name] := FoundValue[0]».

Ну вот и все на сегодня. Используйте FixInsight, чтобы найти баги до того как ваши пользователи их найдут :)