Add lint check for implicit conversions that change a constant's value.

This occurs when the constant value is out of range of the type being assigned to. This is likely indicative of an error, or of code that assumes types have larger ranges than they do in ORCA/C (e.g. 32-bit int).

This intentionally does not report cases where a value is assigned to a signed type but is within the range of the corresponding unsigned type, or vice versa. These may be done intentionally, e.g. setting an unsigned value to "-1" or setting a signed value using a hex constant with the high bit set. Also, only conversions to 8-bit or 16-bit integer types are currently checked.
This commit is contained in:
Stephen Heumann 2023-01-03 18:56:46 -06:00
parent 9f36e99194
commit 245dd0a3f4
5 changed files with 50 additions and 3 deletions

2
CC.rez
View File

@ -11,5 +11,5 @@ resource rVersion(1) {
verUS, /* Region code */
"ORCA/C", /* Short version number */
"Copyright 1997, Byte Works, Inc.\n" /* Long version number */
"Updated 2022"
"Updated 2023"
};

View File

@ -95,6 +95,7 @@ const
lintC99Syntax = $0040; {check for syntax that C99 disallows}
lintReturn = $0080; {flag issues with how functions return}
lintUnused = $0100; {check for unused variables}
lintConstantRange = $0200; {check for out-of-range constants}
{bit masks for GetLInfo flags}
{----------------------------}

View File

@ -610,6 +610,41 @@ var
baseType1,baseType2: baseTypeEnum; {temp variables (for speed)}
kind1,kind2: typeKind; {temp variables (for speed)}
procedure CheckConstantRange(t1: typePtr; value: longint);
{ Check for situations where an implicit conversion will }
{ change the value of a constant. }
{ }
{ Note: This currently only addresses conversions to 8-bit }
{ or 16-bit integer types, and intentionally does not }
{ distinguish between signed and unsigned types. }
var
min,max: longint; {min/max allowed values}
begin {CheckConstantRange}
if t1^.cType = ctBool then begin
min := 0;
max := 1;
end {if}
else if t1^.baseType in [cgByte,cgUByte] then begin
min := -128;
max := 255;
end {else if}
else if t1^.baseType in [cgWord,cgUWord] then begin
min := -32768;
max := 65536;
end {else if}
else begin
min := -maxint4-1;
max := maxint4;
end; {else}
if (value < min) or (value > max) then
Error(186);
end; {CheckConstantRange}
begin {AssignmentConversion}
kind1 := t1^.kind;
kind2 := t2^.kind;
@ -630,6 +665,9 @@ else if kind2 in
case kind1 of
scalarType: begin
if ((lint & lintConstantRange) <> 0) then
if isConstant then
CheckConstantRange(t1, value);
baseType1 := t1^.baseType;
if baseType1 in [cgReal,cgDouble,cgComp] then
baseType1 := cgExtended;
@ -701,6 +739,9 @@ else if kind2 in
enumType: begin
if kind2 = scalarType then begin
if ((lint & lintConstantRange) <> 0) then
if isConstant then
CheckConstantRange(intPtr, value);
baseType2 := t2^.baseType;
if baseType2 in [cgString,cgVoid] then
Error(47)

View File

@ -202,7 +202,7 @@ const
{----}
defaultName = '13:ORCACDefs:Defaults.h'; {default include file name}
maxErr = 10; {max errors on one line}
maxLint = 185; {maximum lint error code}
maxLint = 186; {maximum lint error code}
type
errorType = record {record of a single error}
@ -801,6 +801,7 @@ if list or (numErr <> 0) then begin
183: msg := @'array index out of bounds';
184: msg := @'segment exceeds bank size';
185: msg := @'lint: unused variable: ';
186: msg := @'lint: implicit conversion changes value of constant';
otherwise: Error(57);
end; {case}
if extraStr <> nil then begin
@ -4530,7 +4531,7 @@ strictMode := false; {...with extensions}
{error codes for lint messages}
{if changed, also change maxLint}
lintErrors :=
[51,104,105,110,124,125,128,129,130,147,151,152,153,154,155,170,185];
[51,104,105,110,124,125,128,129,130,147,151,152,153,154,155,170,185,186];
spaceStr := ' '; {strings used in stringization}
quoteStr := '"';

View File

@ -761,6 +761,10 @@ If #pragma lint bit 7 (a value of 128) is set, ORCA/C detects some situations wh
If #pragma lint bit 8 (a value of 256) is set, ORCA/C checks for variables that are declared but never subsequently used. This covers local variables in functions, as well as file-scope static variables.
* Checking for implicit conversions that change the value of a constant:
If #pragma lint bit 9 (a value of 512) is set, ORCA/C checks for certain situations where an implicit conversion will change the value of a constant. Implicit conversions occur in assignments, where the value of the expression is converted to the type of the location being assigned to. They also occur in some related situations like initializers and return statements. This lint check identifies cases where a constant is used in such a situation, but its value is outside the range of the type it is converted to, and therefore the value will be changed by the implicit conversion. This may be indicative of a programming error, or of code written with the assumption that types have larger ranges than they do in ORCA/C. This check currently only identifies cases where out-of-range integer values are converted to an 8-bit or 16-bit integer type.
#pragma extensions
------------------