Reputation: 9106
If I compile the below code it gives a warning on line 1:
value assigned to lTime never used*
But if I remove that line, I get a different warning on line 2:
variable lTime might not have been initialized
Is the compiler missing something, or am I?
procedure TFormWebServices.RemoveOldReports;
var
TSR : TSearchRec;
I : Integer;
lCutOff,
lTime : Int64;
TSToDelete: TStringList;
S,Msg : String;
E : Exception;
begin
lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
I := FindFirst(FReportDir + '*.pdf',0,TSR);
TSToDelete := TStringList.Create;
while I = 0 do
begin
if (TSR.Attr and faDirectory) = 0 then
begin
lTime := lCutOff; // Line 1
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then // Line 2
TSToDelete.Add(TSR.Name);
end;
I := FindNext(TSR);
end;
This is not a dupe of Why is the Compiler warning that variable may not be initialized?, because I assign lTime
in the exception as well.
Upvotes: 4
Views: 257
Reputation: 613481
lTime := lCutOff;
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then
TSToDelete.Add(TSR.Name);
The compiler is correct to warn about this code. When you assign lTime := lCutOff
on the first line, the value written in that assignment is never read.
However, when you remove the code things are less clear cut.
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then
TSToDelete.Add(TSR.Name);
There are two scenarios to consider: an exception is not raised inside the try/except
block, or an exception is raised there.
If no exception is raised then it is simple, lTime
is assigned the result of StrToInt64
and is therefore initialized before being read.
In an exception is raised, then lTime
is not initialized inside the block. What happens next?
Exception
(not compulsory) then it is caught and lTime
is initialized.Exception
then it is not caught and lTime
is never read.Therefore, the compiler could infer all of that and therefore not issue an uninitialized variable warning. However, the compiler does not do flow analysis of that complexity, sadly. I believe that its logic runs like this:
lTime
is initialized, thenlTime
is then read, and still might be uninitialized.In step 2 it ought to be able to realise that lTime
is initialized but it simply does not perform such analysis. So whilst one could argue that the compiler could do better, you just have to accept this as a limitation of its analysis algorithms.
Having understood that we are left with the task of finding a way to write the code and avoid the warnings. We don't want to suppress the warnings, we just need to find a way to write the code so that it is both correct and free of warnings.
The way forward, in my view, is to recognise that exceptions are the wrong tool to use here. It is normal behaviour for this conversion to fail. You should write the code using conversion functions that don't raise an exception when they fail. For instance you could use one of the following options:
if TryStrToInt64(..., lTime) then
if lTime < lCutOff then
....
else
lTime := lCutoff;
Or:
lTime := StrToInt64Def(..., lCutoff);
if lTime < lCutOff then
....
Upvotes: 7
Reputation: 216
This is correct behaviour. The line 1 value will never be used since in the try/except you assign a new value without using the previous one, hence the warning.
if you remove the line you try to use the lTime variable outside a try/except block which may fail without actually setting a value to lTime
procedure TFormWebServices.RemoveOldReports;
var
TSR : TSearchRec;
I : Integer;
lCutOff,
lTime : Int64;
TSToDelete: TStringList;
S,Msg : String;
E : Exception;
begin
lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
I := FindFirst(FReportDir + '*.pdf',0,TSR);
TSToDelete := TStringList.Create;
while I = 0 do
begin
if (TSR.Attr and faDirectory) = 0 then
begin
lTime := lCutOff; // Line 1
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then // Line 2
TSToDelete.Add(TSR.Name);
end;
I := FindNext(TSR);
end;
You could do this
procedure TFormWebServices.RemoveOldReports;
var
TSR : TSearchRec;
I : Integer;
lCutOff,
lTime : Int64;
TSToDelete: TStringList;
S,Msg : String;
E : Exception;
begin
lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
I := FindFirst(FReportDir + '*.pdf',0,TSR);
TSToDelete := TStringList.Create;
while I = 0 do
begin
if (TSR.Attr and faDirectory) = 0 then
begin
lTime := StrToInt64Def(Copy(TSR.Name,1,pos('.',TSR.Name)-1), lCutOff);
if lTime < lCutOff then
TSToDelete.Add(TSR.Name);
end;
I := FindNext(TSR);
end;
Upvotes: -1