Make lint report some more cases where noreturn functions may return.

This uses a heuristic that may produce both false positives and false negatives, but any false positives should reflect extraneous code at the end of the function that is not actually reachable.
This commit is contained in:
Stephen Heumann 2020-01-30 17:30:41 -06:00
parent 76eb476809
commit bc951b6735
4 changed files with 37 additions and 3 deletions

View File

@ -504,6 +504,7 @@ var
useGlobalPool: boolean; {use global (or local) string pool?}
wait: boolean; {wait for keypress after errors?}
lintIsError: boolean; {treat lint messages as errors?}
fIsNoreturn: boolean; {is the current function _Noreturn?}
{syntactic classes of tokens}
{---------------------------}

31
DAG.pas
View File

@ -4821,6 +4821,34 @@ var
DumpLoopLists;
DAGhead := nil; {reset the DAG pointers}
end; {Generate}
procedure CheckNoreturn;
{ Check if a noreturn function looks like it might return. }
{ }
{ This uses a heuristic of basically looking for code at the }
{ end of the function that would lead to it returning if }
{ executed. Control flow operations are optimistically }
{ assumed not to lead to a return. This may produce both }
{ false positives and false negative, but any false }
{ positives should reflect extraneous code that is not }
{ actually reachable (which is dubious in its own right). }
var
code: icptr;
begin {CheckNoreturn}
code := DAGhead;
while code^.opcode in [pc_nop,pc_ret,pc_lnm,dc_lab,dc_loc,pc_add] do
code := code^.next;
while code^.opcode = pc_pop do
code := code^.left;
while code^.opcode = pc_bno do
code := code^.right;
if not (code^.opcode in [pc_fjp,pc_tjp,pc_ujp,pc_xjp,pc_cui,pc_cup,pc_tl1])
then Error(154);
end; {CheckNoreturn}
procedure Push (code: icptr);
@ -4940,6 +4968,9 @@ case code^.opcode of
end;
dc_enp: begin
if fIsNoreturn then
if (lint & lintNoreturn) <> 0 then
CheckNoreturn;
Push(code);
Reverse;
Generate;

View File

@ -154,7 +154,6 @@ var
doingMain: boolean; {are we processing the main function?}
firstCompoundStatement: boolean; {are we doing a function level compound statement?}
fType: typePtr; {return type of the current function}
fIsNoreturn: boolean; {is the current function _Noreturn?}
isForwardDeclared: boolean; {is the field list component }
{ referencing a forward struct/union? }
isFunction: boolean; {is the declaration a function?}
@ -352,6 +351,7 @@ if not doingFunction then begin {if so, finish it off}
dumpLocal := true; {dump the local pool}
nameFound := false; {no pc_nam for the next function (yet)}
volatile := savedVolatile; {local volatile vars are out of scope}
fIsNoreturn := false; {not doing a noreturn function}
end; {if}
PopTable; {remove this symbol table}
dispose(stPtr); {dump the record}
@ -4364,6 +4364,7 @@ nameFound := false; {no pc_nam generated yet}
statementList := nil; {no open statements}
codegenStarted := false; {code generator is not started}
doingForLoopClause1 := false; {not doing a for loop}
fIsNoreturn := false; {not doing a noreturn function}
{init syntactic classes of tokens}
{See C17 section 6.7 ff.}

View File

@ -182,7 +182,7 @@ const
{----}
defaultName = '13:ORCACDefs:Defaults.h'; {default include file name}
maxErr = 10; {max errors on one line}
maxLint = 153; {maximum lint error code}
maxLint = 154; {maximum lint error code}
type
errorType = record {record of a single error}
@ -680,6 +680,7 @@ if list or (numErr <> 0) then begin
151: msg := @'lint: type specifier missing';
152: msg := @'lint: return with no value in non-void function';
153: msg := @'lint: return statement in function declared _Noreturn';
154: msg := @'lint: function declared _Noreturn can return or has unreachable code';
otherwise: Error(57);
end; {case}
writeln(msg^);
@ -3653,7 +3654,7 @@ lintIsError := true; {lint messages are considered errors}
{error codes for lint messages}
{if changed, also change maxLint}
lintErrors := [51,104,105,110,124,125,128,129,130,147,151,152,153];
lintErrors := [51,104,105,110,124,125,128,129,130,147,151,152,153,154];
spaceStr := ' '; {strings used in stringization}
quoteStr := '"';