Add lint option to check for unused variables.

This commit is contained in:
Stephen Heumann 2022-12-12 21:47:32 -06:00
parent 44499bdddb
commit fe62f70d51
8 changed files with 105 additions and 2 deletions

2
CC.pas
View File

@ -140,6 +140,8 @@ DoGlobals; {create the ~GLOBALS and ~ARRAYS segment
{shut down the compiler}
TermHeader; {make sure the compiled header file is closed}
CheckStaticFunctions; {check for undefined functions}
if (lint & lintUnused) <> 0 then {check for unused static vars}
CheckUnused(globalTable);
ffDCBGS.action := 7; {purge the source file}
ffDCBGS.pcount := 14;
ffDCBGS.pathName := @includeFileGS.theString;

View File

@ -93,6 +93,7 @@ const
lintOverflow = $0020; {check for overflows}
lintC99Syntax = $0040; {check for syntax that C99 disallows}
lintReturn = $0080; {flag issues with how functions return}
lintUnused = $0100; {check for unused variables}
{bit masks for GetLInfo flags}
{----------------------------}
@ -375,6 +376,7 @@ type
iPtr: initializerPtr; {pointer to the first initializer}
isForwardDeclared: boolean; {does this var use a forward declared type?}
class: tokenEnum; {storage class}
used: boolean; {is this identifier used?}
case storage: storageType of
stackFrame: (lln: integer; {local label #}
clnext: identPtr); {next compound literal}

View File

@ -986,6 +986,7 @@ var
stack^.token.class := longlongConstant;
stack^.token.kind := longlongconst;
stack^.token.qval := longlong0;
id := nil;
end {if}
{if the id is not declared, create a function returning integer}
@ -1022,6 +1023,9 @@ var
stack^.token.kind := intconst;
stack^.token.ival := id^.itype^.eval;
end; {else if}
if id <> nil then
id^.used := true;
stack^.id := id; {save the identifier}
ComplexTerm; {handle subscripts, selection, etc.}
1:

View File

@ -1810,6 +1810,7 @@ var
sp^.isForwardDeclared := boolean(ReadByte);
sp^.class := tokenEnum(ReadByte);
sp^.storage := storageType(ReadByte);
sp^.used := false;
if sp^.storage = none then
sp^.anonMemberField := false
else if sp^.storage = external then

View File

@ -4115,6 +4115,7 @@ if isFunction then begin
lp := NewSymbol(@'__orcac_va_info', vaInfoPtr, autosy,
variableSpace, declared, false);
lp^.lln := GetLocalLabel;
lp^.used := true;
Gen2(dc_loc, lp^.lln, ord(vaInfoPtr^.size));
Gen2(pc_lda, lastParameterLLN, lastParameterSize);
Gen2t(pc_cop, lp^.lln, 0, cgULong);

View File

@ -114,6 +114,18 @@ procedure Error (err: integer);
{ err - error number }
procedure ErrorWithExtraString (err:integer; extraStr: stringPtr);
{ flag an error, with an extra string to be attached to it }
{ }
{ err - error number }
{ extraStr - extra string to include in error message }
{ }
{ Note: }
{ extraStr must point to a pString allocated with new. }
{ This call transfers ownership of it. }
{procedure Error2 (loc, err: integer); {debug}
{ flag an error }
@ -190,13 +202,14 @@ const
{----}
defaultName = '13:ORCACDefs:Defaults.h'; {default include file name}
maxErr = 10; {max errors on one line}
maxLint = 170; {maximum lint error code}
maxLint = 185; {maximum lint error code}
type
errorType = record {record of a single error}
num: integer; {error number}
line: longint; {line number}
col: integer; {column number}
extraStr: stringPtr; {extra text to include in message}
end;
{file inclusion}
@ -787,8 +800,13 @@ if list or (numErr <> 0) then begin
182: msg := @'''='' expected';
183: msg := @'array index out of bounds';
184: msg := @'segment exceeds bank size';
185: msg := @'lint: unused variable: ';
otherwise: Error(57);
end; {case}
if extraStr <> nil then begin
extraStr^ := concat(msg^,extraStr^);
msg := extraStr;
end; {if}
writeln(msg^);
if terminalErrors and (numErrors <> 0)
and (lintIsError or not (num in lintErrors)) then begin
@ -803,6 +821,8 @@ if list or (numErr <> 0) then begin
else
TermError(0);
end; {if}
if extraStr <> nil then
dispose(extraStr);
end; {with}
{handle pauses}
if ((numErr <> 0) and wait) or KeyPress then begin
@ -3767,12 +3787,31 @@ else begin
with errors[numErr] do begin {record the position of the error}
line := tokenLine;
col := tokenColumn;
extraStr := nil;
end; {with}
if numErrors <> 0 then
codeGeneration := false; {inhibit code generation}
end; {Error}
procedure ErrorWithExtraString {err:integer; extraStr: stringPtr};
{ flag an error, with an extra string to be attached to it }
{ }
{ err - error number }
{ extraStr - extra string to include in error message }
{ }
{ Note: }
{ extraStr must point to a pString allocated with new. }
{ This call transfers ownership of it. }
begin {ErrorWithExtraString}
Error(err);
if errors[numErr].num <> 4 then
errors[numErr].extraStr := extraStr;
end; {ErrorWithExtraString}
{procedure Error2 {loc, err: integer} {debug}
{ flag an error }
@ -4490,7 +4529,8 @@ 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];
lintErrors :=
[51,104,105,110,124,125,128,129,130,147,151,152,153,154,155,170,185];
spaceStr := ' '; {strings used in stringization}
quoteStr := '"';

View File

@ -100,6 +100,11 @@ procedure CheckStaticFunctions;
{ check for undefined functions }
procedure CheckUnused (tPtr: symbolTablePtr);
{ check for unused variables in symbol table }
function CompTypes (t1, t2: typePtr): boolean;
{ Determine if the two types are compatible }
@ -2339,6 +2344,7 @@ if needSymbol then begin
{p^.isForwardDeclared := false;} {assume no forward declarations are used}
p^.name := name; {record the name}
{p^.next := nil;}
{p^.used := false;} {unused for now}
if space <> fieldListSpace then {insert the symbol in the hash bucket}
begin
if itype = nil then
@ -2389,6 +2395,47 @@ useGlobalPool := lUseGlobalPool; {restore the useGlobalPool variable}
end; {NewSymbol}
procedure CheckUnused {tPtr: symbolTablePtr};
{ check for unused variables in symbol table }
var
i: integer; {loop variable}
ip: identPtr; {current symbol}
nameStr: stringPtr;
begin {CheckUnused}
for i := 0 to hashSize do begin {loop over all hash buckets}
ip := tPtr^.buckets[i]; {trace through non-static symbols}
while ip <> nil do begin
if not ip^.used then
if ip^.itype <> nil then
if not (ip^.itype^.kind in [functionType,enumConst]) then
if ip^.storage in [stackFrame,private] then
if not (ip^.name^[1] in ['~','@']) then begin
new(nameStr);
nameStr^ := ip^.name^;
ErrorWithExtraString(185, nameStr);
end; {if}
ip := ip^.next;
end; {while}
ip := globalTable^.buckets[i]; {trace through static symbols}
while ip <> nil do begin
if not ip^.used then
if ip^.itype <> nil then
if not (ip^.itype^.kind in [functionType,enumConst]) then
if ip^.storage = private then
if copy(ip^.name^,1,5) = tPtr^.staticNum then begin
new(nameStr);
nameStr^ := copy(ip^.name^, 6, maxint);
ErrorWithExtraString(185, nameStr);
end; {if}
ip := ip^.next;
end; {while}
end; {for}
end; {CheckUnused}
procedure PopTable;
{ Pop a symbol table (remove definitions local to a block) }
@ -2400,6 +2447,8 @@ begin {PopTable}
tPtr := table;
{if printSymbols then {debug}
{ PrintTable(tPtr); {debug}
if (lint & lintUnused) <> 0 then
CheckUnused(tPtr);
if tPtr^.next <> nil then begin
table := table^.next;
dispose(tPtr);

View File

@ -757,6 +757,10 @@ ORCA/C can now detect several elements of C syntax that were allowed in C89 but
If #pragma lint bit 7 (a value of 128) is set, ORCA/C detects some situations where a function with a non-void return type may return an unpredictable value, either by executing a return statement with no value or by executing to the end of the function with no return statement. (The former case is also detected by #pragma lint bit 6.) It also detects some situations where a _Noreturn function could return. Note that these checks only detect some cases of these problems, not all of them. Also, they may report a potential problem in some situations where the code at the end of a function is not actually reachable.
* Checking for unused variables:
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.
#pragma extensions
------------------