В этом посте я продолжу анализ библиотек поставляемых с 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, чтобы найти баги до того как ваши пользователи их найдут :)
1. Styx
27 Апр 2015 5:01 пп
> Format( ‘Value: %0:*.*f (%1:*.*f..%2:*.*f)’, [Value, Min, Max]);
> Строка форматирования неправильная.
А что не так?
2. Роман Янковский
27 Апр 2015 5:06 пп
Звездочки — это тоже параметры.