mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-10-14 05:46:01 +02:00
+ support for verifying whether a case statements handles all possibilities
(based on patch by Martok) o enabled by default in ISO and Extended Pascal: compile-time error if not the case in ISO mode, warning and run-time error in Extended Pascal mode o warning enabled by default in all other modes for boolean, enumeration and subrange integer types with ranges different from the default ones (i.e., different from 0..255, -128..127, 0..65536, etc) o warnings for all ordinal types can be enabled in all modes with -CC git-svn-id: trunk@42047 -
This commit is contained in:
parent
281b3ad276
commit
ba1b4b1c92
9
.gitattributes
vendored
9
.gitattributes
vendored
@ -13005,6 +13005,15 @@ tests/test/tcase6.pp svneol=native#text/pascal
|
|||||||
tests/test/tcase7.pp svneol=native#text/pascal
|
tests/test/tcase7.pp svneol=native#text/pascal
|
||||||
tests/test/tcase8.pp svneol=native#text/pascal
|
tests/test/tcase8.pp svneol=native#text/pascal
|
||||||
tests/test/tcase9.pp svneol=native#text/pascal
|
tests/test/tcase9.pp svneol=native#text/pascal
|
||||||
|
tests/test/tcasecov1.pp svneol=native#text/plain
|
||||||
|
tests/test/tcasecov2.pp svneol=native#text/plain
|
||||||
|
tests/test/tcasecov3.pp svneol=native#text/plain
|
||||||
|
tests/test/tcasecov4.pp svneol=native#text/plain
|
||||||
|
tests/test/tcasecov5.pp svneol=native#text/plain
|
||||||
|
tests/test/tcasecov6.pp svneol=native#text/plain
|
||||||
|
tests/test/tcasecov7.pp svneol=native#text/plain
|
||||||
|
tests/test/tcasecov8.pp svneol=native#text/plain
|
||||||
|
tests/test/tcasecov9.pp svneol=native#text/plain
|
||||||
tests/test/tcg1.pp svneol=native#text/plain
|
tests/test/tcg1.pp svneol=native#text/plain
|
||||||
tests/test/tchlp1.pp svneol=native#text/pascal
|
tests/test/tchlp1.pp svneol=native#text/pascal
|
||||||
tests/test/tchlp10.pp svneol=native#text/pascal
|
tests/test/tchlp10.pp svneol=native#text/pascal
|
||||||
|
@ -157,6 +157,7 @@ interface
|
|||||||
cs_do_inline,cs_fpu_fwait,cs_ieee_errors,
|
cs_do_inline,cs_fpu_fwait,cs_ieee_errors,
|
||||||
cs_check_low_addr_load,cs_imported_data,
|
cs_check_low_addr_load,cs_imported_data,
|
||||||
cs_excessprecision,cs_check_fpu_exceptions,
|
cs_excessprecision,cs_check_fpu_exceptions,
|
||||||
|
cs_check_all_case_coverage,
|
||||||
{ mmx }
|
{ mmx }
|
||||||
cs_mmx,cs_mmx_saturation,
|
cs_mmx,cs_mmx_saturation,
|
||||||
{ parser }
|
{ parser }
|
||||||
|
@ -2365,7 +2365,7 @@ sym_e_type_must_be_rec_or_object=05098_E_Record or object type expected
|
|||||||
#
|
#
|
||||||
# Codegenerator
|
# Codegenerator
|
||||||
#
|
#
|
||||||
# 06049 is the last used one
|
# 06060 is the last used one
|
||||||
#
|
#
|
||||||
% \section{Code generator messages}
|
% \section{Code generator messages}
|
||||||
% This section lists all messages that can be displayed if the code
|
% This section lists all messages that can be displayed if the code
|
||||||
@ -2520,6 +2520,9 @@ cg_n_no_inline=06058_N_Call to subroutine "$1" marked as inline is not inlined
|
|||||||
% The directive inline is only a hint to the compiler. Sometimes the compiler ignores this hint, a subroutine
|
% The directive inline is only a hint to the compiler. Sometimes the compiler ignores this hint, a subroutine
|
||||||
% marked as inline is not inlined. In this case, this hint is given. Compiling with \var{-vd} might result in more information why
|
% marked as inline is not inlined. In this case, this hint is given. Compiling with \var{-vd} might result in more information why
|
||||||
% the directive inline is ignored.
|
% the directive inline is ignored.
|
||||||
|
cg_e_case_incomplete=06059_E_Case statement does not handle all possible cases
|
||||||
|
cg_w_case_incomplete=06060_W_Case statement does not handle all possible cases
|
||||||
|
% The case statement does not contain labels for all possible values of the operand, and no else statement is present.
|
||||||
%
|
%
|
||||||
% \end{description}
|
% \end{description}
|
||||||
# EndOfTeX
|
# EndOfTeX
|
||||||
|
@ -698,6 +698,8 @@ const
|
|||||||
cg_e_function_not_support_by_selected_instruction_set=06056;
|
cg_e_function_not_support_by_selected_instruction_set=06056;
|
||||||
cg_f_max_units_reached=06057;
|
cg_f_max_units_reached=06057;
|
||||||
cg_n_no_inline=06058;
|
cg_n_no_inline=06058;
|
||||||
|
cg_e_case_incomplete=06059;
|
||||||
|
cg_w_case_incomplete=06060;
|
||||||
asmr_d_start_reading=07000;
|
asmr_d_start_reading=07000;
|
||||||
asmr_d_finish_reading=07001;
|
asmr_d_finish_reading=07001;
|
||||||
asmr_e_none_label_contain_at=07002;
|
asmr_e_none_label_contain_at=07002;
|
||||||
@ -1108,9 +1110,9 @@ const
|
|||||||
option_info=11024;
|
option_info=11024;
|
||||||
option_help_pages=11025;
|
option_help_pages=11025;
|
||||||
|
|
||||||
MsgTxtSize = 82926;
|
MsgTxtSize = 83042;
|
||||||
|
|
||||||
MsgIdxMax : array[1..20] of longint=(
|
MsgIdxMax : array[1..20] of longint=(
|
||||||
28,106,351,126,99,59,142,34,221,67,
|
28,106,351,126,99,61,142,34,221,67,
|
||||||
62,20,30,1,1,1,1,1,1,1
|
62,20,30,1,1,1,1,1,1,1
|
||||||
);
|
);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -73,11 +73,6 @@ interface
|
|||||||
jumptable_no_range : boolean;
|
jumptable_no_range : boolean;
|
||||||
{ has the implementation jumptable support }
|
{ has the implementation jumptable support }
|
||||||
min_label : tconstexprint;
|
min_label : tconstexprint;
|
||||||
{ Number of labels }
|
|
||||||
labelcnt: TCgInt;
|
|
||||||
{ Number of individual values checked, counting each value in a range
|
|
||||||
individually (e.g. 0..2 counts as 3). }
|
|
||||||
TrueCount: TCgInt;
|
|
||||||
|
|
||||||
function GetBranchLabel(Block: TNode; out _Label: TAsmLabel): Boolean;
|
function GetBranchLabel(Block: TNode; out _Label: TAsmLabel): Boolean;
|
||||||
|
|
||||||
@ -1138,7 +1133,7 @@ implementation
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
labelarray:=nil;
|
labelarray:=nil;
|
||||||
SetLength(labelarray,case_count_labels(root));
|
SetLength(labelarray,labelcnt);
|
||||||
nextarrayentry:=0;
|
nextarrayentry:=0;
|
||||||
addarrayentry(root);
|
addarrayentry(root);
|
||||||
rebuild(0,high(labelarray),root);
|
rebuild(0,high(labelarray),root);
|
||||||
@ -1148,18 +1143,6 @@ implementation
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure tcgcasenode.pass_generate_code;
|
procedure tcgcasenode.pass_generate_code;
|
||||||
|
|
||||||
{ Combines "case_count_labels" and "case_true_count" }
|
|
||||||
procedure CountBoth(p : pcaselabel);
|
|
||||||
begin
|
|
||||||
Inc(labelcnt);
|
|
||||||
Inc(TrueCount, (p^._high.svalue - p^._low.svalue) + 1);
|
|
||||||
if assigned(p^.less) then
|
|
||||||
CountBoth(p^.less);
|
|
||||||
if assigned(p^.greater) then
|
|
||||||
CountBoth(p^.greater);
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
var
|
||||||
oldflowcontrol: tflowcontrol;
|
oldflowcontrol: tflowcontrol;
|
||||||
i : longint;
|
i : longint;
|
||||||
@ -1232,9 +1215,6 @@ implementation
|
|||||||
else
|
else
|
||||||
{$endif not cpu64bitalu and not cpuhighleveltarget}
|
{$endif not cpu64bitalu and not cpuhighleveltarget}
|
||||||
begin
|
begin
|
||||||
labelcnt := 0;
|
|
||||||
TrueCount := 0;
|
|
||||||
|
|
||||||
if cs_opt_level1 in current_settings.optimizerswitches then
|
if cs_opt_level1 in current_settings.optimizerswitches then
|
||||||
begin
|
begin
|
||||||
{ procedures are empirically passed on }
|
{ procedures are empirically passed on }
|
||||||
@ -1245,8 +1225,6 @@ implementation
|
|||||||
{ ximated as it is not known if rel8, }
|
{ ximated as it is not known if rel8, }
|
||||||
{ rel16 or rel32 jumps are used }
|
{ rel16 or rel32 jumps are used }
|
||||||
|
|
||||||
CountBoth(labels);
|
|
||||||
|
|
||||||
max_label := case_get_max(labels);
|
max_label := case_get_max(labels);
|
||||||
|
|
||||||
{ can we omit the range check of the jump table ? }
|
{ can we omit the range check of the jump table ? }
|
||||||
@ -1280,7 +1258,7 @@ implementation
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
max_dist:=4*TrueCount;
|
max_dist:=4*labelcoverage;
|
||||||
|
|
||||||
{ Don't allow jump tables to get too large }
|
{ Don't allow jump tables to get too large }
|
||||||
if max_dist>4*labelcnt then
|
if max_dist>4*labelcnt then
|
||||||
|
@ -96,9 +96,22 @@ interface
|
|||||||
trangenodeclass = class of trangenode;
|
trangenodeclass = class of trangenode;
|
||||||
|
|
||||||
tcasenode = class(tunarynode)
|
tcasenode = class(tunarynode)
|
||||||
labels : pcaselabel;
|
strict private
|
||||||
|
{ Number of labels }
|
||||||
|
flabelcnt: cardinal;
|
||||||
|
{ Number of individual values checked, counting each value in a range
|
||||||
|
individually (e.g. 0..2 counts as 3). }
|
||||||
|
flabelcoverage: qword;
|
||||||
|
fcountsuptodate: boolean;
|
||||||
|
|
||||||
|
function getlabelcnt: cardinal;
|
||||||
|
function getlabelcoverage: qword;
|
||||||
|
procedure updatecoverage;
|
||||||
|
procedure checkordinalcoverage;
|
||||||
|
public
|
||||||
blocks : TFPList;
|
blocks : TFPList;
|
||||||
elseblock : tnode;
|
elseblock : tnode;
|
||||||
|
|
||||||
constructor create(l:tnode);virtual;
|
constructor create(l:tnode);virtual;
|
||||||
destructor destroy;override;
|
destructor destroy;override;
|
||||||
constructor ppuload(t:tnodetype;ppufile:tcompilerppufile);override;
|
constructor ppuload(t:tnodetype;ppufile:tcompilerppufile);override;
|
||||||
@ -116,6 +129,13 @@ interface
|
|||||||
procedure addlabel(blockid:longint;l,h : tstringconstnode); overload;
|
procedure addlabel(blockid:longint;l,h : tstringconstnode); overload;
|
||||||
procedure addblock(blockid:longint;instr:tnode);
|
procedure addblock(blockid:longint;instr:tnode);
|
||||||
procedure addelseblock(instr:tnode);
|
procedure addelseblock(instr:tnode);
|
||||||
|
|
||||||
|
property labelcnt: cardinal read getlabelcnt;
|
||||||
|
property labelcoverage: qword read getlabelcoverage;
|
||||||
|
protected
|
||||||
|
flabels : pcaselabel;
|
||||||
|
public
|
||||||
|
property labels: pcaselabel read flabels;
|
||||||
end;
|
end;
|
||||||
tcasenodeclass = class of tcasenode;
|
tcasenodeclass = class of tcasenode;
|
||||||
|
|
||||||
@ -125,11 +145,6 @@ interface
|
|||||||
crangenode : trangenodeclass = trangenode;
|
crangenode : trangenodeclass = trangenode;
|
||||||
ccasenode : tcasenodeclass = tcasenode;
|
ccasenode : tcasenodeclass = tcasenode;
|
||||||
|
|
||||||
{ counts the labels }
|
|
||||||
function case_count_labels(root : pcaselabel) : longint;
|
|
||||||
{ Returns the true count in a case block, which includes each individual
|
|
||||||
value in a range (e.g. "0..2" counts as 3) }
|
|
||||||
function case_true_count(root : pcaselabel) : longint;
|
|
||||||
{ searches the highest label }
|
{ searches the highest label }
|
||||||
function case_get_max(root : pcaselabel) : tconstexprint;
|
function case_get_max(root : pcaselabel) : tconstexprint;
|
||||||
{ searches the lowest label }
|
{ searches the lowest label }
|
||||||
@ -139,10 +154,11 @@ interface
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
verbose,
|
verbose,cutils,
|
||||||
symconst,symdef,symsym,symtable,defutil,defcmp,
|
symconst,symdef,symsym,symtable,defutil,defcmp,
|
||||||
htypechk,pass_1,
|
htypechk,pass_1,
|
||||||
nadd,nbas,ncnv,nld,cgbase;
|
nadd,nbas,ncal,ncnv,nld,nutils,
|
||||||
|
cgbase;
|
||||||
|
|
||||||
|
|
||||||
{*****************************************************************************
|
{*****************************************************************************
|
||||||
@ -430,35 +446,14 @@ implementation
|
|||||||
Case Helpers
|
Case Helpers
|
||||||
*****************************************************************************}
|
*****************************************************************************}
|
||||||
|
|
||||||
function case_count_labels(root : pcaselabel) : longint;
|
{ labels is the number of case-labels, while cases includes each individual
|
||||||
var
|
|
||||||
_l : longint;
|
|
||||||
|
|
||||||
procedure count(p : pcaselabel);
|
|
||||||
begin
|
|
||||||
inc(_l);
|
|
||||||
if assigned(p^.less) then
|
|
||||||
count(p^.less);
|
|
||||||
if assigned(p^.greater) then
|
|
||||||
count(p^.greater);
|
|
||||||
end;
|
|
||||||
|
|
||||||
begin
|
|
||||||
_l:=0;
|
|
||||||
count(root);
|
|
||||||
case_count_labels:=_l;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
{ Returns the true count in a case block, which includes each individual
|
|
||||||
value in a range (e.g. "0..2" counts as 3) }
|
value in a range (e.g. "0..2" counts as 3) }
|
||||||
function case_true_count(root : pcaselabel) : longint;
|
procedure case_count_labels(root : pcaselabel; out labels, cases: longint);
|
||||||
var
|
|
||||||
_l : longint;
|
|
||||||
|
|
||||||
procedure count(p : pcaselabel);
|
procedure count(p : pcaselabel);
|
||||||
begin
|
begin
|
||||||
inc(_l, (p^._high.svalue - p^._low.svalue) + 1);
|
inc(labels);
|
||||||
|
inc(cases, (p^._high.svalue - p^._low.svalue) + 1);
|
||||||
if assigned(p^.less) then
|
if assigned(p^.less) then
|
||||||
count(p^.less);
|
count(p^.less);
|
||||||
if assigned(p^.greater) then
|
if assigned(p^.greater) then
|
||||||
@ -466,13 +461,12 @@ implementation
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
_l:=0;
|
labels:=0;
|
||||||
|
cases:=0;
|
||||||
count(root);
|
count(root);
|
||||||
case_true_count:=_l;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function case_get_max(root : pcaselabel) : tconstexprint;
|
function case_get_max(root : pcaselabel) : tconstexprint;
|
||||||
var
|
var
|
||||||
hp : pcaselabel;
|
hp : pcaselabel;
|
||||||
@ -597,7 +591,7 @@ implementation
|
|||||||
constructor tcasenode.create(l:tnode);
|
constructor tcasenode.create(l:tnode);
|
||||||
begin
|
begin
|
||||||
inherited create(casen,l);
|
inherited create(casen,l);
|
||||||
labels:=nil;
|
flabels:=nil;
|
||||||
blocks:=TFPList.create;
|
blocks:=TFPList.create;
|
||||||
elseblock:=nil;
|
elseblock:=nil;
|
||||||
end;
|
end;
|
||||||
@ -609,7 +603,7 @@ implementation
|
|||||||
hp : pcaseblock;
|
hp : pcaseblock;
|
||||||
begin
|
begin
|
||||||
elseblock.free;
|
elseblock.free;
|
||||||
deletecaselabels(labels);
|
deletecaselabels(flabels);
|
||||||
for i:=0 to blocks.count-1 do
|
for i:=0 to blocks.count-1 do
|
||||||
begin
|
begin
|
||||||
pcaseblock(blocks[i])^.statement.free;
|
pcaseblock(blocks[i])^.statement.free;
|
||||||
@ -631,7 +625,9 @@ implementation
|
|||||||
blocks:=TFPList.create;
|
blocks:=TFPList.create;
|
||||||
for i:=0 to cnt-1 do
|
for i:=0 to cnt-1 do
|
||||||
addblock(i,ppuloadnode(ppufile));
|
addblock(i,ppuloadnode(ppufile));
|
||||||
labels:=ppuloadcaselabel(ppufile);
|
flabels:=ppuloadcaselabel(ppufile);
|
||||||
|
{ we don't save/restore the label counts, but recalculate them if needed }
|
||||||
|
fcountsuptodate:=false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -644,7 +640,8 @@ implementation
|
|||||||
ppufile.putlongint(blocks.count);
|
ppufile.putlongint(blocks.count);
|
||||||
for i:=0 to blocks.count-1 do
|
for i:=0 to blocks.count-1 do
|
||||||
ppuwritenode(ppufile,pcaseblock(blocks[i])^.statement);
|
ppuwritenode(ppufile,pcaseblock(blocks[i])^.statement);
|
||||||
ppuwritecaselabel(ppufile,labels);
|
ppuwritecaselabel(ppufile,flabels);
|
||||||
|
{ we don't save/restore the label counts, but recalculate them if needed }
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -686,6 +683,10 @@ implementation
|
|||||||
if assigned(elseblock) then
|
if assigned(elseblock) then
|
||||||
typecheckpass(elseblock);
|
typecheckpass(elseblock);
|
||||||
|
|
||||||
|
if not codegenerror and
|
||||||
|
is_ordinal(left.resultdef) then
|
||||||
|
checkordinalcoverage;
|
||||||
|
|
||||||
resultdef:=voidtype;
|
resultdef:=voidtype;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -726,7 +727,7 @@ implementation
|
|||||||
begin
|
begin
|
||||||
result:=tfpobjectlist.create(true);
|
result:=tfpobjectlist.create(true);
|
||||||
result.count:=blocks.count;
|
result.count:=blocks.count;
|
||||||
add_label_to_blockid_list(result,labels);
|
add_label_to_blockid_list(result,flabels);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function makeifblock(elseblock : tnode): tnode;
|
function makeifblock(elseblock : tnode): tnode;
|
||||||
@ -789,7 +790,7 @@ implementation
|
|||||||
{ Load caseexpr into temp var if complex. }
|
{ Load caseexpr into temp var if complex. }
|
||||||
{ No need to do this for ordinal, because }
|
{ No need to do this for ordinal, because }
|
||||||
{ in that case caseexpr is generated once }
|
{ in that case caseexpr is generated once }
|
||||||
if (labels^.label_type = ltConstString) and (not valid_for_addr(left, false)) and
|
if (flabels^.label_type = ltConstString) and (not valid_for_addr(left, false)) and
|
||||||
(blocks.count > 0) then
|
(blocks.count > 0) then
|
||||||
begin
|
begin
|
||||||
init_block := internalstatements(stmt);
|
init_block := internalstatements(stmt);
|
||||||
@ -832,7 +833,7 @@ implementation
|
|||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if (labels^.label_type = ltConstString) then
|
if (flabels^.label_type = ltConstString) then
|
||||||
begin
|
begin
|
||||||
if_node:=makeifblock(elseblock);
|
if_node:=makeifblock(elseblock);
|
||||||
|
|
||||||
@ -856,41 +857,41 @@ implementation
|
|||||||
case blocks.count of
|
case blocks.count of
|
||||||
2:
|
2:
|
||||||
begin
|
begin
|
||||||
if boolean(qword(labels^._low))=false then
|
if boolean(qword(flabels^._low))=false then
|
||||||
begin
|
begin
|
||||||
node_thenblock:=pcaseblock(blocks[labels^.greater^.blockid])^.statement;
|
node_thenblock:=pcaseblock(blocks[flabels^.greater^.blockid])^.statement;
|
||||||
node_elseblock:=pcaseblock(blocks[labels^.blockid])^.statement;
|
node_elseblock:=pcaseblock(blocks[flabels^.blockid])^.statement;
|
||||||
pcaseblock(blocks[labels^.greater^.blockid])^.statement:=nil;
|
pcaseblock(blocks[flabels^.greater^.blockid])^.statement:=nil;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
node_thenblock:=pcaseblock(blocks[labels^.blockid])^.statement;
|
node_thenblock:=pcaseblock(blocks[flabels^.blockid])^.statement;
|
||||||
node_elseblock:=pcaseblock(blocks[labels^.less^.blockid])^.statement;
|
node_elseblock:=pcaseblock(blocks[flabels^.less^.blockid])^.statement;
|
||||||
pcaseblock(blocks[labels^.less^.blockid])^.statement:=nil;
|
pcaseblock(blocks[flabels^.less^.blockid])^.statement:=nil;
|
||||||
end;
|
end;
|
||||||
pcaseblock(blocks[labels^.blockid])^.statement:=nil;
|
pcaseblock(blocks[flabels^.blockid])^.statement:=nil;
|
||||||
end;
|
end;
|
||||||
1:
|
1:
|
||||||
begin
|
begin
|
||||||
if labels^._low=labels^._high then
|
if flabels^._low=flabels^._high then
|
||||||
begin
|
begin
|
||||||
if boolean(qword(labels^._low))=false then
|
if boolean(qword(flabels^._low))=false then
|
||||||
begin
|
begin
|
||||||
node_thenblock:=elseblock;
|
node_thenblock:=elseblock;
|
||||||
node_elseblock:=pcaseblock(blocks[labels^.blockid])^.statement;
|
node_elseblock:=pcaseblock(blocks[flabels^.blockid])^.statement;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
node_thenblock:=pcaseblock(blocks[labels^.blockid])^.statement;
|
node_thenblock:=pcaseblock(blocks[flabels^.blockid])^.statement;
|
||||||
node_elseblock:=elseblock;
|
node_elseblock:=elseblock;
|
||||||
end;
|
end;
|
||||||
pcaseblock(blocks[labels^.blockid])^.statement:=nil;
|
pcaseblock(blocks[flabels^.blockid])^.statement:=nil;
|
||||||
elseblock:=nil;
|
elseblock:=nil;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
result:=pcaseblock(blocks[labels^.blockid])^.statement;
|
result:=pcaseblock(blocks[flabels^.blockid])^.statement;
|
||||||
pcaseblock(blocks[labels^.blockid])^.statement:=nil;
|
pcaseblock(blocks[flabels^.blockid])^.statement:=nil;
|
||||||
elseblock:=nil;
|
elseblock:=nil;
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
@ -911,7 +912,7 @@ implementation
|
|||||||
result:=nil;
|
result:=nil;
|
||||||
if left.nodetype=ordconstn then
|
if left.nodetype=ordconstn then
|
||||||
begin
|
begin
|
||||||
tmp:=labels;
|
tmp:=flabels;
|
||||||
{ check all case labels until we find one that fits }
|
{ check all case labels until we find one that fits }
|
||||||
while assigned(tmp) do
|
while assigned(tmp) do
|
||||||
begin
|
begin
|
||||||
@ -939,6 +940,12 @@ implementation
|
|||||||
{ no else block, so there is no code to execute at all }
|
{ no else block, so there is no code to execute at all }
|
||||||
result:=cnothingnode.create;
|
result:=cnothingnode.create;
|
||||||
end;
|
end;
|
||||||
|
if assigned(elseblock) and
|
||||||
|
has_no_code(elseblock) then
|
||||||
|
begin
|
||||||
|
elseblock.free;
|
||||||
|
elseblock:=nil;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -952,10 +959,10 @@ implementation
|
|||||||
n.elseblock:=elseblock.dogetcopy
|
n.elseblock:=elseblock.dogetcopy
|
||||||
else
|
else
|
||||||
n.elseblock:=nil;
|
n.elseblock:=nil;
|
||||||
if assigned(labels) then
|
if assigned(flabels) then
|
||||||
n.labels:=copycaselabel(labels)
|
n.flabels:=copycaselabel(flabels)
|
||||||
else
|
else
|
||||||
n.labels:=nil;
|
n.flabels:=nil;
|
||||||
if assigned(blocks) then
|
if assigned(blocks) then
|
||||||
begin
|
begin
|
||||||
n.blocks:=TFPList.create;
|
n.blocks:=TFPList.create;
|
||||||
@ -968,6 +975,9 @@ implementation
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
n.blocks:=nil;
|
n.blocks:=nil;
|
||||||
|
n.fcountsuptodate:=fcountsuptodate;
|
||||||
|
n.flabelcnt:=flabelcnt;
|
||||||
|
n.flabelcoverage:=flabelcoverage;
|
||||||
dogetcopy:=n;
|
dogetcopy:=n;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1041,7 +1051,7 @@ implementation
|
|||||||
begin
|
begin
|
||||||
result :=
|
result :=
|
||||||
inherited docompare(p) and
|
inherited docompare(p) and
|
||||||
caselabelsequal(labels,tcasenode(p).labels) and
|
caselabelsequal(flabels,tcasenode(p).flabels) and
|
||||||
caseblocksequal(blocks,tcasenode(p).blocks) and
|
caseblocksequal(blocks,tcasenode(p).blocks) and
|
||||||
elseblock.isequal(tcasenode(p).elseblock);
|
elseblock.isequal(tcasenode(p).elseblock);
|
||||||
end;
|
end;
|
||||||
@ -1066,6 +1076,117 @@ implementation
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function tcasenode.getlabelcnt: cardinal;
|
||||||
|
begin
|
||||||
|
if not fcountsuptodate then
|
||||||
|
updatecoverage;
|
||||||
|
result:=flabelcnt;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function tcasenode.getlabelcoverage: qword;
|
||||||
|
begin
|
||||||
|
if not fcountsuptodate then
|
||||||
|
updatecoverage;
|
||||||
|
result:=flabelcoverage;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure tcasenode.updatecoverage;
|
||||||
|
|
||||||
|
var
|
||||||
|
isord: boolean;
|
||||||
|
|
||||||
|
procedure count(p : pcaselabel);
|
||||||
|
begin
|
||||||
|
inc(flabelcnt);
|
||||||
|
if isord then
|
||||||
|
inc(flabelcoverage, (p^._high.svalue - p^._low.svalue) + 1);
|
||||||
|
if assigned(p^.less) then
|
||||||
|
count(p^.less);
|
||||||
|
if assigned(p^.greater) then
|
||||||
|
count(p^.greater);
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
isord:=is_ordinal(left.resultdef);
|
||||||
|
flabelcnt:=0;
|
||||||
|
flabelcoverage:=0;
|
||||||
|
count(flabels);
|
||||||
|
fcountsuptodate:=true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure tcasenode.checkordinalcoverage;
|
||||||
|
|
||||||
|
function orddefspansfullrange(def: torddef): boolean;
|
||||||
|
var
|
||||||
|
packedbitsize: cardinal;
|
||||||
|
dummy: longint;
|
||||||
|
val: qword;
|
||||||
|
begin
|
||||||
|
result:=false;
|
||||||
|
packedbitsize:=def.packedbitsize;
|
||||||
|
if ((packedbitsize mod 8) <> 0) or
|
||||||
|
not ispowerof2(packedbitsize div 8,dummy) then
|
||||||
|
exit;
|
||||||
|
dec(packedbitsize);
|
||||||
|
if is_signed(def) then
|
||||||
|
begin
|
||||||
|
if def.low<>(-(int64(1) shl packedbitsize)) then
|
||||||
|
exit;
|
||||||
|
if def.high<>((int64(1) shl packedbitsize)-1) then
|
||||||
|
exit;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
if def.low<>0 then
|
||||||
|
exit;
|
||||||
|
val:=qword(1) shl packedbitsize;
|
||||||
|
val:=(val-1)+val;
|
||||||
|
if def.high<>val then
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
result:=true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
lv, hv, typcount: tconstexprint;
|
||||||
|
begin
|
||||||
|
{ Check label type coverage for enumerations and small types }
|
||||||
|
getrange(left.resultdef,lv,hv);
|
||||||
|
typcount:=hv.svalue-lv.svalue+1;
|
||||||
|
if not assigned(elseblock) then
|
||||||
|
begin
|
||||||
|
{ unless cs_check_all_case_coverage is set, only check for enums, booleans and
|
||||||
|
subrange types different from the default ones }
|
||||||
|
if (cs_check_all_case_coverage in current_settings.localswitches) or
|
||||||
|
(is_enum(left.resultdef) or
|
||||||
|
is_boolean(left.resultdef) or
|
||||||
|
not orddefspansfullrange(torddef(left.resultdef))) and
|
||||||
|
(labelcoverage<typcount) then
|
||||||
|
begin
|
||||||
|
{ labels for some values of the operand are missing, and no else block is present }
|
||||||
|
if not(m_iso in current_settings.modeswitches) then
|
||||||
|
begin
|
||||||
|
cgmessage(cg_w_case_incomplete);
|
||||||
|
{ in Extended Pascal, this is a dynamic violation error if it actually happens }
|
||||||
|
if (m_extpas in current_settings.modeswitches) then
|
||||||
|
elseblock:=ccallnode.createintern('fpc_rangeerror',nil);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
{ this is an error in ISO Pascal }
|
||||||
|
message(cg_e_case_incomplete);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else if labelcoverage=typcount then
|
||||||
|
begin
|
||||||
|
{ labels for all values of the operand are present, but an extra else block is present }
|
||||||
|
MessagePos(elseblock.fileinfo, cg_w_unreachable_code);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure tcasenode.addlabel(blockid:longint;const l,h : TConstExprInt);
|
procedure tcasenode.addlabel(blockid:longint;const l,h : TConstExprInt);
|
||||||
var
|
var
|
||||||
hcaselabel : pcaselabel;
|
hcaselabel : pcaselabel;
|
||||||
@ -1120,7 +1241,8 @@ implementation
|
|||||||
hcaselabel^.label_type:=ltOrdinal;
|
hcaselabel^.label_type:=ltOrdinal;
|
||||||
hcaselabel^._low:=l;
|
hcaselabel^._low:=l;
|
||||||
hcaselabel^._high:=h;
|
hcaselabel^._high:=h;
|
||||||
insertlabel(labels);
|
insertlabel(flabels);
|
||||||
|
fcountsuptodate:=false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure tcasenode.addlabel(blockid: longint; l, h: tstringconstnode);
|
procedure tcasenode.addlabel(blockid: longint; l, h: tstringconstnode);
|
||||||
@ -1160,7 +1282,7 @@ implementation
|
|||||||
hcaselabel^._low_str := tstringconstnode(l.getcopy);
|
hcaselabel^._low_str := tstringconstnode(l.getcopy);
|
||||||
hcaselabel^._high_str := tstringconstnode(h.getcopy);
|
hcaselabel^._high_str := tstringconstnode(h.getcopy);
|
||||||
|
|
||||||
insertlabel(labels);
|
insertlabel(flabels);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -2036,6 +2036,11 @@ begin
|
|||||||
exclude(init_settings.moduleswitches,cs_support_c_operators)
|
exclude(init_settings.moduleswitches,cs_support_c_operators)
|
||||||
else
|
else
|
||||||
include(init_settings.moduleswitches,cs_support_c_operators);
|
include(init_settings.moduleswitches,cs_support_c_operators);
|
||||||
|
'C':
|
||||||
|
If UnsetBool(More, j, opt, false) then
|
||||||
|
exclude(init_settings.localswitches,cs_check_all_case_coverage)
|
||||||
|
else
|
||||||
|
include(init_settings.localswitches,cs_check_all_case_coverage);
|
||||||
'd' : //an alternative to -Mdelphi
|
'd' : //an alternative to -Mdelphi
|
||||||
SetCompileMode('DELPHI',true);
|
SetCompileMode('DELPHI',true);
|
||||||
'e' :
|
'e' :
|
||||||
|
@ -360,7 +360,7 @@ unit optutils;
|
|||||||
|
|
||||||
function SetExecutionWeight(var n: tnode; arg: pointer): foreachnoderesult;
|
function SetExecutionWeight(var n: tnode; arg: pointer): foreachnoderesult;
|
||||||
var
|
var
|
||||||
Weight : longint;
|
Weight, CaseWeight : longint;
|
||||||
i : Integer;
|
i : Integer;
|
||||||
begin
|
begin
|
||||||
Result:=fen_false;
|
Result:=fen_false;
|
||||||
@ -370,10 +370,11 @@ unit optutils;
|
|||||||
casen:
|
casen:
|
||||||
begin
|
begin
|
||||||
CalcExecutionWeights(tcasenode(n).left,Weight);
|
CalcExecutionWeights(tcasenode(n).left,Weight);
|
||||||
|
CaseWeight:=max(Weight div tcasenode(n).labelcnt,1);
|
||||||
for i:=0 to tcasenode(n).blocks.count-1 do
|
for i:=0 to tcasenode(n).blocks.count-1 do
|
||||||
CalcExecutionWeights(pcaseblock(tcasenode(n).blocks[i])^.statement,Weight div case_count_labels(tcasenode(n).labels));
|
CalcExecutionWeights(pcaseblock(tcasenode(n).blocks[i])^.statement,CaseWeight);
|
||||||
|
|
||||||
CalcExecutionWeights(tcasenode(n).elseblock,Weight div case_count_labels(tcasenode(n).labels));
|
CalcExecutionWeights(tcasenode(n).elseblock,CaseWeight);
|
||||||
Result:=fen_norecurse_false;
|
Result:=fen_norecurse_false;
|
||||||
end;
|
end;
|
||||||
whilerepeatn:
|
whilerepeatn:
|
||||||
|
@ -436,6 +436,12 @@ unit scandir;
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure dir_checkcasecoverage;
|
||||||
|
begin
|
||||||
|
do_localswitch(cs_check_all_case_coverage);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure dir_checkfpuexceptions;
|
procedure dir_checkfpuexceptions;
|
||||||
begin
|
begin
|
||||||
do_localswitch(cs_check_fpu_exceptions);
|
do_localswitch(cs_check_fpu_exceptions);
|
||||||
@ -1908,6 +1914,7 @@ unit scandir;
|
|||||||
AddDirective('BOOLEVAL',directive_all, @dir_booleval);
|
AddDirective('BOOLEVAL',directive_all, @dir_booleval);
|
||||||
AddDirective('BITPACKING',directive_all, @dir_bitpacking);
|
AddDirective('BITPACKING',directive_all, @dir_bitpacking);
|
||||||
AddDirective('CALLING',directive_all, @dir_calling);
|
AddDirective('CALLING',directive_all, @dir_calling);
|
||||||
|
AddDirective('CHECKCASECOVERAGE',directive_all, @dir_checkcasecoverage);
|
||||||
AddDirective('CHECKFPUEXCEPTIONS',directive_all, @dir_checkfpuexceptions);
|
AddDirective('CHECKFPUEXCEPTIONS',directive_all, @dir_checkfpuexceptions);
|
||||||
AddDirective('CHECKLOWADDRLOADS',directive_all, @dir_checklowaddrloads);
|
AddDirective('CHECKLOWADDRLOADS',directive_all, @dir_checklowaddrloads);
|
||||||
AddDirective('CHECKPOINTER',directive_all, @dir_checkpointer);
|
AddDirective('CHECKPOINTER',directive_all, @dir_checkpointer);
|
||||||
|
@ -521,12 +521,17 @@ implementation
|
|||||||
|
|
||||||
HandleModeSwitches(m_none,changeinit);
|
HandleModeSwitches(m_none,changeinit);
|
||||||
|
|
||||||
{ turn on bitpacking for mode macpas and iso pascal as well as extended pascal }
|
{ turn on bitpacking and case checking for mode macpas and iso pascal,
|
||||||
|
as well as extended pascal }
|
||||||
if ([m_mac,m_iso,m_extpas] * current_settings.modeswitches <> []) then
|
if ([m_mac,m_iso,m_extpas] * current_settings.modeswitches <> []) then
|
||||||
begin
|
begin
|
||||||
include(current_settings.localswitches,cs_bitpacking);
|
include(current_settings.localswitches,cs_bitpacking);
|
||||||
|
include(current_settings.localswitches,cs_check_all_case_coverage);
|
||||||
if changeinit then
|
if changeinit then
|
||||||
include(init_settings.localswitches,cs_bitpacking);
|
begin
|
||||||
|
include(init_settings.localswitches,cs_bitpacking);
|
||||||
|
include(init_settings.localswitches,cs_check_all_case_coverage);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ support goto/label by default in delphi/tp7/mac/iso/extpas modes }
|
{ support goto/label by default in delphi/tp7/mac/iso/extpas modes }
|
||||||
|
@ -129,7 +129,7 @@ implementation
|
|||||||
{ Limit size of jump tables for small enumerations so they have
|
{ Limit size of jump tables for small enumerations so they have
|
||||||
to be at least two-thirds full before being considered for the
|
to be at least two-thirds full before being considered for the
|
||||||
"almost exhaustive" treatment }
|
"almost exhaustive" treatment }
|
||||||
ExhaustiveLimit := min(ExhaustiveLimitBase, TrueCount shl 1)
|
ExhaustiveLimit := min(ExhaustiveLimitBase, labelcoverage shl 1)
|
||||||
else
|
else
|
||||||
ExhaustiveLimit := ExhaustiveLimitBase;
|
ExhaustiveLimit := ExhaustiveLimitBase;
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ implementation
|
|||||||
{ Limit size of jump tables for small enumerations so they have
|
{ Limit size of jump tables for small enumerations so they have
|
||||||
to be at least two-thirds full before being considered for the
|
to be at least two-thirds full before being considered for the
|
||||||
"almost exhaustive" treatment }
|
"almost exhaustive" treatment }
|
||||||
ExhaustiveLimit := min(ExhaustiveLimitBase, TrueCount shl 1)
|
ExhaustiveLimit := min(ExhaustiveLimitBase, labelcoverage shl 1)
|
||||||
else
|
else
|
||||||
ExhaustiveLimit := ExhaustiveLimitBase;
|
ExhaustiveLimit := ExhaustiveLimitBase;
|
||||||
|
|
||||||
|
11
tests/test/tcasecov1.pp
Normal file
11
tests/test/tcasecov1.pp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{ %fail }
|
||||||
|
{$mode iso}
|
||||||
|
|
||||||
|
var
|
||||||
|
l: longint;
|
||||||
|
begin
|
||||||
|
l:=1;
|
||||||
|
case l of
|
||||||
|
2: writeln;
|
||||||
|
end;
|
||||||
|
end.
|
12
tests/test/tcasecov2.pp
Normal file
12
tests/test/tcasecov2.pp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{ %fail }
|
||||||
|
{ %opt=-Sew }
|
||||||
|
{$mode extendedpascal}
|
||||||
|
|
||||||
|
var
|
||||||
|
l: longint;
|
||||||
|
begin
|
||||||
|
l:=1;
|
||||||
|
case l of
|
||||||
|
2: writeln;
|
||||||
|
end;
|
||||||
|
end.
|
11
tests/test/tcasecov3.pp
Executable file
11
tests/test/tcasecov3.pp
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
{ %result=201 }
|
||||||
|
{$mode extendedpascal}
|
||||||
|
|
||||||
|
var
|
||||||
|
l: longint;
|
||||||
|
begin
|
||||||
|
l:=1;
|
||||||
|
case l of
|
||||||
|
2: writeln;
|
||||||
|
end;
|
||||||
|
end.
|
11
tests/test/tcasecov4.pp
Executable file
11
tests/test/tcasecov4.pp
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
{ %opt=-Sew }
|
||||||
|
{ %norun }
|
||||||
|
|
||||||
|
var
|
||||||
|
l: longint;
|
||||||
|
begin
|
||||||
|
l:=1;
|
||||||
|
case l of
|
||||||
|
2: writeln;
|
||||||
|
end;
|
||||||
|
end.
|
12
tests/test/tcasecov5.pp
Executable file
12
tests/test/tcasecov5.pp
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
{ %opt=-Sew }
|
||||||
|
{ %norun }
|
||||||
|
|
||||||
|
{ should not print a warning }
|
||||||
|
var
|
||||||
|
l: qword;
|
||||||
|
begin
|
||||||
|
l:=1;
|
||||||
|
case l of
|
||||||
|
2: writeln;
|
||||||
|
end;
|
||||||
|
end.
|
12
tests/test/tcasecov6.pp
Executable file
12
tests/test/tcasecov6.pp
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
{ %fail }
|
||||||
|
{ %opt=-Sew }
|
||||||
|
{ %norun }
|
||||||
|
|
||||||
|
var
|
||||||
|
l: 0..3;
|
||||||
|
begin
|
||||||
|
l:=1;
|
||||||
|
case l of
|
||||||
|
2: writeln;
|
||||||
|
end;
|
||||||
|
end.
|
11
tests/test/tcasecov7.pp
Executable file
11
tests/test/tcasecov7.pp
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
{ %opt=-Sew }
|
||||||
|
{ %norun }
|
||||||
|
|
||||||
|
var
|
||||||
|
s: shortstring;
|
||||||
|
begin
|
||||||
|
s:='abc';
|
||||||
|
case s[1] of
|
||||||
|
'b': writeln;
|
||||||
|
end;
|
||||||
|
end.
|
13
tests/test/tcasecov8.pp
Executable file
13
tests/test/tcasecov8.pp
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
{ %opt=-Sew }
|
||||||
|
{ %norun }
|
||||||
|
|
||||||
|
const
|
||||||
|
OT_SIZE_MASK = $3000001F;
|
||||||
|
var
|
||||||
|
l: longint;
|
||||||
|
begin
|
||||||
|
l:=1;
|
||||||
|
case l and OT_SIZE_MASK of
|
||||||
|
1: writeln;
|
||||||
|
end;
|
||||||
|
end.
|
15
tests/test/tcasecov9.pp
Executable file
15
tests/test/tcasecov9.pp
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
{ %fail }
|
||||||
|
{ %opt=-Sew }
|
||||||
|
{ %norun }
|
||||||
|
|
||||||
|
var
|
||||||
|
l: 0..1;
|
||||||
|
begin
|
||||||
|
l:=1;
|
||||||
|
case l of
|
||||||
|
0: write('a');
|
||||||
|
1: writeln;
|
||||||
|
else
|
||||||
|
writeln('unreachable');
|
||||||
|
end;
|
||||||
|
end.
|
Loading…
Reference in New Issue
Block a user