Merge pull request #2808 from kugelfuhr/kugelfuhr/flowanalysis

Simple flow analysis to find unreachable code
This commit is contained in:
Bob Andrews
2025-07-26 00:10:21 +02:00
committed by GitHub
28 changed files with 1051 additions and 152 deletions

View File

@@ -29,7 +29,7 @@ int optopt; /* character checked for validity */
char *optarg; /* argument associated with option */
#define tell(s) fputs(*argv,stderr);fputs(s,stderr); \
fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);
fputc(optopt,stderr);fputc('\n',stderr);return(BADCH)
int __fastcall__ getopt (int argc, char* const* argv, const char* optstring)
/* Get option letter from argument vector */

View File

@@ -95,6 +95,7 @@ struct WarnMapEntry {
static WarnMapEntry WarnMap[] = {
/* Keep names sorted, even if it isn't used for now */
{ &WarnConstComparison, "const-comparison" },
{ &WarnConstOverflow, "const-overflow" },
{ &WarningsAreErrors, "error" },
{ &WarnNoEffect, "no-effect" },
{ &WarnPointerSign, "pointer-sign" },
@@ -108,7 +109,6 @@ static WarnMapEntry WarnMap[] = {
{ &WarnUnusedLabel, "unused-label" },
{ &WarnUnusedParam, "unused-param" },
{ &WarnUnusedVar, "unused-var" },
{ &WarnConstOverflow, "const-overflow" },
};
Collection DiagnosticStrBufs;
@@ -142,7 +142,7 @@ void PrintFileInclusionInfo (const LineInfo* LI)
static LineInfo* GetDiagnosticLI (void)
LineInfo* GetDiagnosticLI (void)
/* Get the line info where the diagnostic info refers to */
{
if (CurTok.LI) {
@@ -184,7 +184,7 @@ static unsigned GetDiagnosticLineNum (void)
void Fatal_ (const char *file, int line, const char* Format, ...)
void Fatal_ (const char* file, int line, const char* Format, ...)
/* Print a message about a fatal error and die */
{
va_list ap;
@@ -208,7 +208,7 @@ void Fatal_ (const char *file, int line, const char* Format, ...)
void Internal_ (const char *file, int line, const char* Format, ...)
void Internal_ (const char* file, int line, const char* Format, ...)
/* Print a message about an internal compiler error and die */
{
va_list ap;
@@ -279,7 +279,7 @@ static void IntError (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap)
void LIError_ (const char *file, int line, errcat_t EC, LineInfo* LI, const char* Format, ...)
void LIError_ (const char* file, int line, errcat_t EC, LineInfo* LI, const char* Format, ...)
/* Print an error message with the line info given explicitly */
{
va_list ap;
@@ -295,7 +295,7 @@ void LIError_ (const char *file, int line, errcat_t EC, LineInfo* LI, const char
void Error_ (const char *file, int line, const char* Format, ...)
void Error_ (const char* file, int line, const char* Format, ...)
/* Print an error message */
{
va_list ap;
@@ -311,7 +311,7 @@ void Error_ (const char *file, int line, const char* Format, ...)
void PPError_ (const char *file, int line, const char* Format, ...)
void PPError_ (const char* file, int line, const char* Format, ...)
/* Print an error message. For use within the preprocessor */
{
va_list ap;
@@ -370,7 +370,7 @@ static void IntWarning (errcat_t EC, LineInfo* LI, const char* Msg, va_list ap)
void LIWarning_ (const char *file, int line, errcat_t EC, LineInfo* LI, const char* Format, ...)
void LIWarning_ (const char* file, int line, errcat_t EC, LineInfo* LI, const char* Format, ...)
/* Print a warning message with the line info given explicitly */
{
va_list ap;
@@ -386,7 +386,7 @@ void LIWarning_ (const char *file, int line, errcat_t EC, LineInfo* LI, const ch
void Warning_ (const char *file, int line, const char* Format, ...)
void Warning_ (const char* file, int line, const char* Format, ...)
/* Print a warning message */
{
va_list ap;
@@ -402,7 +402,7 @@ void Warning_ (const char *file, int line, const char* Format, ...)
void PPWarning_ (const char *file, int line, const char* Format, ...)
void PPWarning_ (const char* file, int line, const char* Format, ...)
/* Print a warning message. For use within the preprocessor */
{
va_list ap;
@@ -424,7 +424,45 @@ void UnreachableCodeWarning (void)
*/
{
if (IS_Get (&WarnUnreachableCode)) {
Warning ("Unreachable code");
LineInfo* LI;
/* Add special handling for compound statements if the current token
** is from the source. Doing this here is a bit hacky but unfortunately
** there's no better place.
*/
if (CurTok.LI && NextTok.LI) {
if (CurTok.Tok == TOK_LCURLY) {
/* Do not point to the compound statement but to the first
** statement within it. If the compound statement is empty
** do not even output a warning. This fails of course for
** nested compounds but will do the right thing in most cases.
*/
if (NextTok.Tok == TOK_RCURLY) {
return;
}
LI = NextTok.LI;
} else {
LI = CurTok.LI;
}
} else {
LI = GetCurLineInfo ();
}
/* Now output the warning */
LIWarning (EC_PARSER, LI, "Unreachable code");
}
}
void LIUnreachableCodeWarning (LineInfo* LI)
/* Print a warning about unreachable code at the given location if these
** warnings are enabled.
*/
{
if (IS_Get (&WarnUnreachableCode)) {
LIWarning (EC_PARSER, LI, "Unreachable code");
}
}

View File

@@ -103,6 +103,9 @@ struct StrBuf;
void PrintFileInclusionInfo (const LineInfo* LI);
/* Print hierarchy of file inclusion */
LineInfo* GetDiagnosticLI (void);
/* Get the line info where the diagnostic info refers to */
void Fatal_ (const char *file, int line, const char* Format, ...) attribute ((noreturn, format (printf, 3, 4)));
#define Fatal(...) Fatal_(__FILE__, __LINE__, __VA_ARGS__)
/* Print a message about a fatal error and die */
@@ -140,6 +143,11 @@ void UnreachableCodeWarning (void);
** warnings are enabled.
*/
void LIUnreachableCodeWarning (LineInfo* LI);
/* Print a warning about unreachable code at the given location if these
** warnings are enabled.
*/
IntStack* FindWarning (const char* Name);
/* Search for a warning in the WarnMap table and return a pointer to the
** intstack that holds its state. Return NULL if there is no such warning.

View File

@@ -453,6 +453,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
SymEntry* Param;
const Type* RType; /* Real type used for struct parameters */
const Type* ReturnType; /* Return type */
int StmtFlags; /* Flow control flags for the function compound */
/* Remember this function descriptor used for definition */
GetFuncDesc (Func->Type)->FuncDef = D;
@@ -626,13 +627,13 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
*/
CurrentFunc->TopLevelSP = StackPtr;
/* Now process statements in this block */
while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) {
AnyStatement (0);
}
/* Now process statements in this block checking for unreachable code */
StmtFlags = StatementBlock (0);
/* Check if this function is missing a return value */
if (!F_HasVoidReturn (CurrentFunc) && !F_HasReturn (CurrentFunc)) {
if (!SF_Unreach (StmtFlags) &&
!F_HasVoidReturn (CurrentFunc) &&
!F_HasReturn (CurrentFunc)) {
/* If this is the main function in a C99 environment returning an int,
** let it always return zero. Otherwise output a warning.
*/

View File

@@ -41,10 +41,12 @@
#include "error.h"
#include "exprdesc.h"
#include "expr.h"
#include "function.h"
#include "loadexpr.h"
#include "scanner.h"
#include "seqpoint.h"
#include "standard.h"
#include "stmt.h"
#include "symtab.h"
#include "goto.h"
@@ -56,8 +58,8 @@
void GotoStatement (void)
/* Process a goto statement. */
int GotoStatement (void)
/* Process a goto statement and return one of the SF_xxx flags from stmt.h. */
{
/* Eat the "goto" */
NextToken ();
@@ -165,6 +167,8 @@ void GotoStatement (void)
} else {
Error ("Label name expected");
}
return SF_GOTO | SF_ANY_GOTO;
}

View File

@@ -44,8 +44,8 @@
void GotoStatement (void);
/* Process a goto statement. */
int GotoStatement (void);
/* Process a goto statement and return one of the SF_xxx flags from stmt.h. */
void DoLabel (void);
/* Define a goto label. */

View File

@@ -104,7 +104,7 @@ LineInfo* GetCurLineInfo (void);
void UpdateCurrentLineInfo (const StrBuf* Line);
/* Update the current line info - called if a new line is read */
void RememberCheckedLI (struct LineInfo* LI);
void RememberCheckedLI (LineInfo* LI);
/* Remember the latest checked line info struct */
LineInfo* GetPrevCheckedLI (void);

View File

@@ -154,7 +154,7 @@ static int IfStatement (void)
{
unsigned Label1;
unsigned TestResult;
int GotBreak;
int StmtFlags;
/* Skip the if */
NextToken ();
@@ -163,21 +163,33 @@ static int IfStatement (void)
Label1 = GetLocalLabel ();
TestResult = TestInParens (Label1, 0);
/* Output a warning if the condition is always false */
if (TestResult == TESTEXPR_FALSE) {
UnreachableCodeWarning ();
}
/* Parse the if body */
GotBreak = AnyStatement (0);
StmtFlags = AnyStatement (0, 0);
/* Else clause present? */
if (CurTok.Tok != TOK_ELSE) {
g_defcodelabel (Label1);
/* Since there's no else clause, we're not sure, if the a break
** statement is really executed.
/* If the test result is always true, any special statement (return,
** break etc.) is always executed. If not, we cannot be sure.
*/
return 0;
if (TestResult != TESTEXPR_TRUE) {
/* There's no else so we're not sure, if any special statements
** are really executed.
*/
StmtFlags &= ~SF_MASK_UNREACH;
}
} else {
int StmtFlags2;
/* Generate a jump around the else branch */
unsigned Label2 = GetLocalLabel ();
g_jump (Label2);
@@ -195,22 +207,40 @@ static int IfStatement (void)
/* Define the target for the first test */
g_defcodelabel (Label1);
/* Total break only if both branches had a break. */
GotBreak &= AnyStatement (0);
/* Parse the else body and evaluate special statements */
StmtFlags2 = AnyStatement (0, 0);
if (TestResult == TESTEXPR_FALSE) {
/* The "else" part is always executed */
StmtFlags = StmtFlags2;
} else if (TestResult != TESTEXPR_TRUE) {
/* If both branches have a special statement the following code is
** unreachable and we combine the flags. Otherwise the code
** following the "if" is always reachable.
*/
StmtFlags |= SF_Any (StmtFlags2);
if (SF_Unreach (StmtFlags) && SF_Unreach (StmtFlags2)) {
StmtFlags |= SF_Unreach (StmtFlags2);
} else {
StmtFlags &= ~SF_MASK_UNREACH;
}
}
/* Generate the label for the else clause */
g_defcodelabel (Label2);
/* Done */
return GotBreak;
}
/* Done */
return StmtFlags;
}
static void DoStatement (void)
static int DoStatement (void)
/* Handle the 'do' statement */
{
int StmtFlags;
unsigned TestResult;
/* Get the loop control labels */
unsigned LoopLabel = GetLocalLabel ();
unsigned BreakLabel = GetLocalLabel ();
@@ -226,14 +256,14 @@ static void DoStatement (void)
g_defcodelabel (LoopLabel);
/* Parse the loop body */
AnyStatement (0);
StmtFlags = AnyStatement (0, 0);
/* Output the label for a continue */
g_defcodelabel (ContinueLabel);
/* Parse the end condition */
Consume (TOK_WHILE, "'while' expected");
TestInParens (LoopLabel, 1);
TestResult = TestInParens (LoopLabel, 1);
ConsumeSemi ();
/* Define the break label */
@@ -241,17 +271,52 @@ static void DoStatement (void)
/* Remove the loop from the loop stack */
DelLoop ();
/* Fix the flags for the loop. */
if (TestResult == TESTEXPR_TRUE) {
/* If the loop condition is always true, and we do not have a
** "break" statement, the loop won't terminate. So the only valid
** "unreach" flag is that for the endless loop. Otherwise - if there
** is a "break" statement, the code after the loop is reachable.
*/
StmtFlags &= ~SF_MASK_UNREACH;
if (!SF_Any_Break (StmtFlags)) {
StmtFlags |= (SF_OTHER | SF_ANY_OTHER);
}
} else if (SF_Any_Break (StmtFlags)) {
/* If the loop condition is not always true, but we have a "break"
** anywhere, the following code is reachable.
*/
StmtFlags &= ~SF_MASK_UNREACH;
} else {
/* Otherwise the last statement in the loop determines the status for
** the following code, so we have to change "continue" into "other"
** but apart from that flags stay as they are.
*/
if (SF_Continue (StmtFlags)) {
StmtFlags &= ~SF_CONTINUE;
StmtFlags |= (SF_OTHER | SF_ANY_OTHER);
}
}
/* "break" and "continue" are not relevant for the following code */
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE);
/* Done */
return StmtFlags;
}
static void WhileStatement (void)
static int WhileStatement (void)
/* Handle the 'while' statement */
{
int PendingToken;
CodeMark CondCodeStart; /* Start of condition evaluation code */
CodeMark CondCodeEnd; /* End of condition evaluation code */
CodeMark Here; /* "Here" location of code */
unsigned TestResult; /* Result of the while loop test expression */
int StmtFlags;
/* Get the loop control labels */
unsigned LoopLabel = GetLocalLabel ();
@@ -274,8 +339,15 @@ static void WhileStatement (void)
/* Remember the current position */
GetCodePos (&CondCodeStart);
/* Test the loop condition */
TestInParens (LoopLabel, 1);
/* Test the loop condition. While loops are somewhat different from other
** loops: While an "always true" condition is used often for endless loops
** (or loops left by "break"), an "always false" condition doesn't make
** sense, so we check that here and warn about it.
*/
TestResult = TestInParens (LoopLabel, 1);
if (TestResult == TESTEXPR_FALSE) {
UnreachableCodeWarning ();
}
/* Remember the end of the condition evaluation code */
GetCodePos (&CondCodeEnd);
@@ -284,7 +356,7 @@ static void WhileStatement (void)
g_defcodelabel (LoopLabel);
/* Loop body */
AnyStatement (&PendingToken);
StmtFlags = AnyStatement (&PendingToken, 0);
/* Emit the while condition label */
g_defcodelabel (CondLabel);
@@ -303,11 +375,34 @@ static void WhileStatement (void)
/* Remove the loop from the loop stack */
DelLoop ();
/* Fix the flags for the loop. */
if (TestResult == TESTEXPR_TRUE) {
/* If the loop condition is always true, and we do not have a
** "break" statement, the loop won't terminate. So the only valid
** "unreach" flag is that for the endless loop. Otherwise - if there
** is a "break" statement, the code after the loop is reachable.
*/
StmtFlags &= ~SF_MASK_UNREACH;
if (!SF_Any_Break (StmtFlags)) {
StmtFlags |= (SF_OTHER | SF_ANY_OTHER);
}
} else {
/* If the loop condition is not always true, the code after the loop
** is always reachable.
*/
StmtFlags &= ~SF_MASK_UNREACH;
}
/* "break" and "continue" are not relevant for the following code */
StmtFlags &= ~(SF_ANY_BREAK | SF_ANY_CONTINUE);
/* Done */
return StmtFlags;
}
static void ReturnStatement (void)
static int ReturnStatement (void)
/* Handle the 'return' statement */
{
ExprDesc Expr;
@@ -357,11 +452,14 @@ static void ReturnStatement (void)
/* Output a jump to the function exit code */
g_jump (F_GetRetLab (CurrentFunc));
/* Done */
return SF_RETURN | SF_ANY_RETURN;
}
static void BreakStatement (void)
static int BreakStatement (void)
/* Handle the 'break' statement */
{
LoopDesc* L;
@@ -376,7 +474,7 @@ static void BreakStatement (void)
if (L == 0) {
/* Error: No current loop */
Error ("'break' statement not within loop or switch");
return;
return SF_NONE;
}
/* Correct the stack pointer if needed */
@@ -384,11 +482,14 @@ static void BreakStatement (void)
/* Jump to the exit label of the loop */
g_jump (L->BreakLabel);
/* Done */
return SF_BREAK | SF_ANY_BREAK;
}
static void ContinueStatement (void)
static int ContinueStatement (void)
/* Handle the 'continue' statement */
{
LoopDesc* L;
@@ -411,7 +512,7 @@ static void ContinueStatement (void)
/* Did we find it? */
if (L == 0) {
Error ("'continue' statement not within a loop");
return;
return SF_NONE;
}
/* Correct the stackpointer if needed */
@@ -419,17 +520,22 @@ static void ContinueStatement (void)
/* Jump to next loop iteration */
g_jump (L->ContinueLabel);
/* Done */
return SF_CONTINUE | SF_ANY_CONTINUE;
}
static void ForStatement (void)
static int ForStatement (void)
/* Handle a 'for' statement */
{
int HaveIncExpr;
CodeMark IncExprStart;
CodeMark IncExprEnd;
int PendingToken;
unsigned TestResult;
int StmtFlags;
/* Get several local labels needed later */
unsigned TestLabel = GetLocalLabel ();
@@ -463,9 +569,10 @@ static void ForStatement (void)
/* Parse the test expression */
if (CurTok.Tok != TOK_SEMI) {
Test (BodyLabel, 1);
TestResult = Test (BodyLabel, 1);
g_jump (BreakLabel);
} else {
TestResult = TESTEXPR_TRUE;
g_jump (BodyLabel);
}
ConsumeSemi ();
@@ -497,7 +604,7 @@ static void ForStatement (void)
/* Loop body */
g_defcodelabel (BodyLabel);
AnyStatement (&PendingToken);
StmtFlags = AnyStatement (&PendingToken, 0);
/* If we had an increment expression, move the code to the bottom of
** the loop. In this case we don't need to jump there at the end of
@@ -520,16 +627,21 @@ static void ForStatement (void)
/* Remove the loop from the loop stack */
DelLoop ();
/* If the condition is always true, any special statements are always
** executed. Otherwise we don't know.
*/
return (TestResult == TESTEXPR_TRUE)? StmtFlags : SF_NONE;
}
static int CompoundStatement (int* PendingToken)
static int CompoundStatement (int* PendingToken, struct SwitchCtrl* Switch)
/* Compound statement. Allow any number of statements inside braces. The
** function returns true if the last statement was a break or return.
*/
{
int GotBreak = 0;
int StmtFlags;
/* Remember the stack at block entry */
int OldStack = StackPtr;
@@ -544,17 +656,11 @@ static int CompoundStatement (int* PendingToken)
/* Parse local variable declarations if any */
DeclareLocals ();
/* Now process statements in this block */
while (CurTok.Tok != TOK_RCURLY) {
if (CurTok.Tok != TOK_CEOF) {
GotBreak = AnyStatement (0);
} else {
break;
}
}
/* Now process statements in this block checking for unreachable code */
StmtFlags = StatementBlock (Switch);
/* Clean up the stack if the codeflow may reach the end */
if (!GotBreak) {
if ((StmtFlags & SF_MASK_UNREACH) == SF_NONE) {
g_space (StackPtr - OldStack);
}
@@ -564,7 +670,6 @@ static int CompoundStatement (int* PendingToken)
if (OldBlockStackSize != CollCount (&CurrentFunc->LocalsBlockStack)) {
CollPop (&CurrentFunc->LocalsBlockStack);
}
StackPtr = OldStack;
/* Emit references to imports/exports for this block */
@@ -576,12 +681,13 @@ static int CompoundStatement (int* PendingToken)
/* Skip '}' */
CheckTok (TOK_RCURLY, "'}' expected", PendingToken);
return GotBreak;
/* Done */
return StmtFlags;
}
static void Statement (int* PendingToken)
static int Statement (int* PendingToken)
/* Single-line statement */
{
ExprDesc Expr;
@@ -615,54 +721,73 @@ static void Statement (int* PendingToken)
}
CheckSemi (PendingToken);
/* Done. All special statements are handled in other subroutines. */
return SF_NONE;
}
static int ParseAnyLabels (void)
/* Return -1 if there are any labels with a statement */
{
unsigned PrevErrorCount = ErrorCount;
int HasLabels = 0;
for (;;) {
if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
/* C 'goto' label */
DoLabel ();
} else if (CurTok.Tok == TOK_CASE) {
/* C 'case' label */
CaseLabel ();
} else if (CurTok.Tok == TOK_DEFAULT) {
/* C 'default' label */
DefaultLabel ();
} else {
/* No labels */
break;
}
HasLabels = 1;
}
if (HasLabels) {
if (PrevErrorCount != ErrorCount || CheckLabelWithoutStatement ()) {
return -1;
}
}
return 0;
}
int AnyStatement (int* PendingToken)
/* Statement parser. Returns 1 if the statement does a return/break, returns
** 0 otherwise. If the PendingToken pointer is not NULL, the function will
** not skip the terminating token of the statement (closing brace or
** semicolon), but store true if there is a pending token, and false if there
** is none. The token is always checked, so there is no need for the caller to
** check this token, it must be skipped, however. If the argument pointer is
** NULL, the function will skip the token.
int StatementBlock (struct SwitchCtrl* Switch)
/* Parse multiple statements within curly braces checking for unreachable
** code. Returns the SF_xxx flags for the last statement.
*/
{
int GotBreak = 0;
/* We want to emit an "unreachable code" warning for statements following
** a "goto", "return" etc. But only - and this is what complicates it -
** if the following statement is not preceeded by a label. Since the latter
** means that a jump may go there so the statement is actually reachable.
*/
if (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) {
LineInfo* LI1 = UseLineInfo (GetDiagnosticLI ());
int StmtFlags1 = AnyStatement (0, Switch);
int UnreachableWarning = 0;
while (CurTok.Tok != TOK_RCURLY && CurTok.Tok != TOK_CEOF) {
LineInfo* LI2 = UseLineInfo (GetDiagnosticLI ());
int StmtFlags2 = AnyStatement (0, Switch);
if (!UnreachableWarning) {
/* Check if the code is unreachable because of a preceeding
** jump and if the code doesn't have a jump label.
*/
if ((StmtFlags1 & SF_MASK_UNREACH) != SF_NONE &&
(StmtFlags2 & SF_MASK_LABEL) == SF_NONE) {
LIUnreachableCodeWarning (LI2);
UnreachableWarning = 1;
}
}
if (LI1) {
ReleaseLineInfo (LI1);
}
LI1 = LI2;
StmtFlags1 = (StmtFlags1 & SF_MASK_ANY) | StmtFlags2;
}
if (LI1) {
ReleaseLineInfo (LI1);
}
return StmtFlags1;
} else {
return SF_NONE;
}
}
int AnyStatement (int* PendingToken, struct SwitchCtrl* Switch)
/* Statement parser. Returns one of the SF_xxx flags describing if the
** statement does a return/break. If the PendingToken pointer is not NULL,
** the function will not skip the terminating token of the statement (closing
** brace or semicolon), but store true if there is a pending token, and false
** if there is none. The token is always checked, so there is no need for the
** caller to check this token, it must be skipped, however. If the argument
** pointer is NULL, the function will skip the token. When called to parse a
** switch body, the switch control structure must be passed via the Switch
** argument. Otherwise it must be NULL.
*/
{
int LabelFlags = SF_NONE;
int StmtFlags;
unsigned PrevErrorCount;
LineInfo* LI;
/* Assume no pending token */
if (PendingToken) {
@@ -670,75 +795,113 @@ int AnyStatement (int* PendingToken)
}
/* Handle any labels. A label is always part of a statement, it does not
** replace one.
** replace one. If we have errors parsing labels, return without reading
** the following statement.
*/
if (ParseAnyLabels ()) {
return 0;
PrevErrorCount = ErrorCount;
while (1) {
if (CurTok.Tok == TOK_IDENT && NextTok.Tok == TOK_COLON) {
/* C 'goto' label */
DoLabel ();
LabelFlags |= SF_LABEL_GOTO;
} else if (CurTok.Tok == TOK_CASE) {
/* C 'case' label */
CaseLabel ();
LabelFlags |= SF_LABEL_CASE;
} else if (CurTok.Tok == TOK_DEFAULT) {
/* C 'default' label */
DefaultLabel ();
LabelFlags |= SF_LABEL_DEFAULT;
} else {
/* No labels */
break;
}
}
if (LabelFlags != SF_NONE) {
/* We had labels, check for errors */
if (PrevErrorCount != ErrorCount || CheckLabelWithoutStatement ()) {
return SF_NONE;
}
}
/* Remember the line info for the now following statement */
LI = UseLineInfo (GetDiagnosticLI ());
/* Now look at the actual statement. */
switch (CurTok.Tok) {
case TOK_IF:
GotBreak = IfStatement ();
StmtFlags = IfStatement ();
break;
case TOK_SWITCH:
SwitchStatement ();
StmtFlags = SwitchStatement ();
break;
case TOK_WHILE:
WhileStatement ();
StmtFlags = WhileStatement ();
break;
case TOK_DO:
DoStatement ();
StmtFlags = DoStatement ();
break;
case TOK_FOR:
ForStatement ();
StmtFlags = ForStatement ();
break;
case TOK_GOTO:
GotoStatement ();
StmtFlags = GotoStatement ();
CheckSemi (PendingToken);
GotBreak = 1;
break;
case TOK_RETURN:
ReturnStatement ();
StmtFlags = ReturnStatement ();
CheckSemi (PendingToken);
GotBreak = 1;
break;
case TOK_BREAK:
BreakStatement ();
StmtFlags = BreakStatement ();
CheckSemi (PendingToken);
GotBreak = 1;
break;
case TOK_CONTINUE:
ContinueStatement ();
StmtFlags = ContinueStatement ();
CheckSemi (PendingToken);
GotBreak = 1;
break;
case TOK_SEMI:
/* Empty statement. Ignore it */
CheckSemi (PendingToken);
StmtFlags = SF_NONE;
break;
case TOK_LCURLY:
GotBreak = CompoundStatement (PendingToken);
StmtFlags = CompoundStatement (PendingToken, Switch);
break;
default:
/* Simple statement */
Statement (PendingToken);
StmtFlags = Statement (PendingToken);
break;
}
/* The flags for labels returned by subroutines are invalid in most cases,
** so we remove them and use the ones determined before.
*/
StmtFlags = (StmtFlags & ~SF_MASK_LABEL) | LabelFlags;
/* Reset SQP flags */
SetSQPFlags (SQP_KEEP_NONE);
return GotBreak;
/* If we're inside a switch, do tracking of special statements */
if (Switch) {
SwitchBodyStatement (Switch, LI, StmtFlags);
}
/* Release the line info we remembered above */
ReleaseLineInfo (LI);
/* Done */
return StmtFlags;
}

View File

@@ -38,20 +38,128 @@
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Enumeration for the AnyStatement() return flags. It is used for simple flow
** analysis and tracks occurance of "return", "break", "goto" and "continue"
** (anything that jumps). The flags for the distinct statements are only set
** if they are always executed. So for example in case of an if/else statement,
** SF_RETURN is set only if both branches contain a "return". The SF_ANY_xxx
** flags are set if any of the statements occurred. So for an if/else
** statement, if one branch contains a "return" and the other a "continue"
** statement, neither SF_RETURN, nor SF_CONTINUE is set, but SF_ANY_RETURN and
** SF_ANY_CONTINUE.
** There are some additional flags that tell if the statement parsed was
** preceeded by at least one label.
*/
enum {
SF_NONE = 0x0000,
/* Flags for special statements that cause the next statement to be
** unreachable. They are only return as long as the condition is true.
** Please note that a statement function can return more than one of
** these flags set. For example for an "if" that has "return" in one
** branch and "continue" in another.
*/
SF_RETURN = 0x00001, /* Code definitely returns */
SF_BREAK = 0x00002, /* Code definitely breaks */
SF_GOTO = 0x00004, /* Code definitely jumps */
SF_CONTINUE = 0x00008, /* Code definitely continues */
SF_OTHER = 0x00010, /* Endless loop, terminating func */
SF_MASK_UNREACH = 0x000FF, /* Following code is unreachable */
/* Flags for the occurance of any of the conditions from above. Statements
** will clear whatever is no longer valid when exiting. A while loop, for
** example will never return SF_ANY_BREAK or SF_ANY_CONTINUE since that was
** handled inside the loop.
*/
SF_ANY_RETURN = 0x00100, /* Code contains a return statement */
SF_ANY_BREAK = 0x00200, /* Code contains a break statement */
SF_ANY_GOTO = 0x00400, /* Code contains a goto statement */
SF_ANY_CONTINUE = 0x00800, /* Code contains a continue statement */
SF_ANY_OTHER = 0x01000, /* Code contains endless loop etc. */
SF_MASK_ANY = 0x0FF00, /* Code contains any of the above */
/* Flags for labels */
SF_LABEL_GOTO = 0x10000, /* Statement preceeded by goto label */
SF_LABEL_CASE = 0x20000, /* Statement preceeded by case label */
SF_LABEL_DEFAULT = 0x40000, /* Statement preceeded by default label */
SF_MASK_LABEL = 0x70000, /* Mask for any label */
};
/* Forward */
struct SwitchCtrl;
/*****************************************************************************/
/* Functions to handle the flow control flags */
/*****************************************************************************/
static inline int SF_Break (int F)
/* Return just the "break" flag in F */
{
return (F & SF_BREAK);
}
static inline int SF_Continue (int F)
/* Return just the "continue" flag in F */
{
return (F & SF_CONTINUE);
}
static inline int SF_Unreach (int F)
/* Return just the "unreachable" part of the given flags */
{
return (F & SF_MASK_UNREACH);
}
static inline int SF_Any_Break (int F)
/* Check if there was any "break" statement */
{
return (F & SF_ANY_BREAK);
}
static inline int SF_Any (int F)
/* Return just the "any" part of the given flags */
{
return (F & SF_MASK_ANY);
}
static inline int SF_Label (int F)
/* Return just the "label" part of the given flags */
{
return (F & SF_MASK_LABEL);
}
/*****************************************************************************/
/* Code */
/*****************************************************************************/
int AnyStatement (int* PendingToken);
/* Statement parser. Returns 1 if the statement does a return/break, returns
** 0 otherwise. If the PendingToken pointer is not NULL, the function will
** not skip the terminating token of the statement (closing brace or
** semicolon), but store true if there is a pending token, and false if there
** is none. The token is always checked, so there is no need for the caller to
** check this token, it must be skipped, however. If the argument pointer is
** NULL, the function will skip the token.
int StatementBlock (struct SwitchCtrl* Switch);
/* Parse multiple statements within curly braces checking for unreachable
** code. Returns the SF_xxx flags for the last statement.
*/
int AnyStatement (int* PendingToken, struct SwitchCtrl* Switch);
/* Statement parser. Returns one of the SF_xxx flags describing if the
** statement does a return/break. If the PendingToken pointer is not NULL,
** the function will not skip the terminating token of the statement (closing
** brace or semicolon), but store true if there is a pending token, and false
** if there is none. The token is always checked, so there is no need for the
** caller to check this token, it must be skipped, however. If the argument
** pointer is NULL, the function will skip the token. When called to parse a
** switch body, the switch control structure must be passed via the Switch
** argument. Otherwise it must be NULL.
*/

View File

@@ -61,15 +61,23 @@
/* Some bitmapped flags for use in SwitchCtrl */
#define SC_NONE 0x0000
#define SC_CASE 0x0001 /* We had a case label */
#define SC_DEFAULT 0x0002 /* We had a default label */
#define SC_MASK_LABEL 0x0003 /* Mask for the labels */
#define SC_WEIRD 0x0004 /* Flag for a weird switch that contains code
** outside of any label.
*/
typedef struct SwitchCtrl SwitchCtrl;
struct SwitchCtrl {
Collection* Nodes; /* CaseNode tree */
const Type* ExprType; /* Switch controlling expression type */
unsigned Depth; /* Number of bytes the selector type has */
unsigned DefaultLabel; /* Label for the default branch */
unsigned CtrlFlags; /* Bitmapped flags as defined above */
int StmtFlags; /* Collected statement flags */
};
/* Pointer to current switch control struct */
@@ -77,14 +85,60 @@ static SwitchCtrl* Switch = 0;
/*****************************************************************************/
/* Functions that work with struct SwitchExpr */
/*****************************************************************************/
static int SC_Label (const SwitchCtrl* S)
/* Check if we had a switch label */
{
return (S->CtrlFlags & SC_MASK_LABEL) != SC_NONE;
}
static int SC_IsWeird (const SwitchCtrl* S)
/* Check if this switch is weird */
{
return (S->CtrlFlags & SC_WEIRD) != SC_NONE;
}
static void SC_SetCase (SwitchCtrl* S)
/* Set the current label type to "case" */
{
S->CtrlFlags |= SC_CASE;
}
static void SC_SetDefault (SwitchCtrl* S)
/* Set the current label type to "default" */
{
S->CtrlFlags |= SC_DEFAULT;
}
static void SC_MakeWeird (SwitchCtrl* S)
/* Mark the switch as weird */
{
S->CtrlFlags |= SC_WEIRD;
}
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void SwitchStatement (void)
/* Handle a switch statement for chars with a cmp cascade for the selector */
int SwitchStatement (void)
/* Handle a 'switch' statement and return the corresponding SF_xxx flags */
{
ExprDesc SwitchExpr; /* Switch statement expression */
CodeMark CaseCodeStart; /* Start of code marker */
@@ -92,7 +146,7 @@ void SwitchStatement (void)
CodeMark SwitchCodeEnd; /* End of switch code */
unsigned ExitLabel; /* Exit label */
unsigned SwitchCodeLabel;/* Label for the switch code */
int HaveBreak = 0; /* True if the last statement had a break */
int StmtFlags; /* True if the last statement had a break */
int RCurlyBrace; /* True if last token is right curly brace */
SwitchCtrl* OldSwitch; /* Pointer to old switch control data */
SwitchCtrl SwitchData; /* New switch data */
@@ -136,6 +190,8 @@ void SwitchStatement (void)
SwitchData.ExprType = SwitchExpr.Type;
SwitchData.Depth = SizeOf (SwitchExpr.Type);
SwitchData.DefaultLabel = 0;
SwitchData.CtrlFlags = SC_NONE;
SwitchData.StmtFlags = SF_NONE;
OldSwitch = Switch;
Switch = &SwitchData;
@@ -148,7 +204,7 @@ void SwitchStatement (void)
/* Parse the following statement, which may actually be a compound
** statement if there is a curly brace at the current input position
*/
HaveBreak = AnyStatement (&RCurlyBrace);
StmtFlags = AnyStatement (&RCurlyBrace, Switch);
/* Check if we had any labels */
if (CollCount (SwitchData.Nodes) == 0 && SwitchData.DefaultLabel == 0) {
@@ -162,7 +218,7 @@ void SwitchStatement (void)
** carry the label), add a jump to the exit. If it is useless, the
** optimizer will remove it later.
*/
if (!HaveBreak) {
if (SF_Unreach (StmtFlags) == SF_NONE) {
g_jump (ExitLabel);
}
@@ -201,6 +257,11 @@ void SwitchStatement (void)
if (RCurlyBrace) {
NextToken ();
}
/* We only return the combined "any" flags from all the statements within
** the switch. Minus "break" which is handled inside the switch.
*/
return SwitchData.StmtFlags & ~SF_ANY_BREAK;
}
@@ -268,6 +329,9 @@ void CaseLabel (void)
Warning (DiagMsg, CaseExpr.IVal);
}
/* Remember that we're in a case label section now */
SC_SetCase (Switch);
} else {
/* case keyword outside a switch statement */
@@ -297,6 +361,9 @@ void DefaultLabel (void)
Switch->DefaultLabel = GetLocalLabel ();
g_defcodelabel (Switch->DefaultLabel);
/* Remember that we're in the default label section now */
SC_SetDefault (Switch);
} else {
/* We had the default label already */
Error ("Multiple default labels in one switch");
@@ -312,3 +379,31 @@ void DefaultLabel (void)
/* Skip the colon */
ConsumeColon ();
}
void SwitchBodyStatement (struct SwitchCtrl* S, LineInfo* LI, int StmtFlags)
/* Helper function for flow analysis. Must be called for all statements within
** a switch passing the flags for special statements.
*/
{
/* The control structure passed must be the current one */
PRECONDITION (S == Switch);
/* Handle code without a label in the switch */
if (SC_Label (S) == SC_NONE) {
/* This is a statement that preceedes any switch labels. If the
** switch is not already marked as weird, output and the current
** statement has no label, output a warning about unreachable code.
*/
if (!SC_IsWeird (S)) {
if (!SF_Label (StmtFlags)) {
LIUnreachableCodeWarning (LI);
}
SC_MakeWeird (S);
}
}
/* Collect the statement flags. */
S->StmtFlags = SF_Any (S->StmtFlags | StmtFlags);
}

View File

@@ -38,14 +38,25 @@
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Forwards */
struct SwitchCtrl;
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void SwitchStatement (void);
/* Handle a 'switch' statement */
int SwitchStatement (void);
/* Handle a 'switch' statement and return the corresponding SF_xxx flags */
void CaseLabel (void);
/* Handle a case label */
@@ -53,6 +64,11 @@ void CaseLabel (void);
void DefaultLabel (void);
/* Handle a default label */
void SwitchBodyStatement (struct SwitchCtrl* Switch, LineInfo* LI, int RetFlags);
/* Helper function for flow analysis. Must be called for all statements within
** a switch passing the flags for special statements.
*/
/* End of swstmt.h */

View File

@@ -53,7 +53,7 @@
unsigned Test (unsigned Label, int Invert)
/* Evaluate a boolean test expression and jump depending on the result of
** the test and on Invert. The function returns one of the TESTEXPR_xx codes
** defined above. If the jump is always true, a warning is output.
** defined above.
*/
{
ExprDesc Expr;
@@ -74,10 +74,7 @@ unsigned Test (unsigned Label, int Invert)
Result = (Expr.IVal != 0) ? TESTEXPR_TRUE : TESTEXPR_FALSE;
/* Constant rvalue */
if (!Invert && Expr.IVal == 0) {
g_jump (Label);
UnreachableCodeWarning ();
} else if (Invert && Expr.IVal != 0) {
if ((!Invert && Expr.IVal == 0) || (Invert && Expr.IVal != 0)) {
g_jump (Label);
}
@@ -125,8 +122,7 @@ unsigned Test (unsigned Label, int Invert)
unsigned TestInParens (unsigned Label, int Invert)
/* Evaluate a boolean test expression in parenthesis and jump depending on
** the result of the test * and on Invert. The function returns one of the
** TESTEXPR_xx codes defined above. If the jump is always true, a warning is
** output.
** TESTEXPR_xx codes defined above.
*/
{
unsigned Result;

View File

@@ -59,14 +59,13 @@
unsigned Test (unsigned Label, int Invert);
/* Evaluate a boolean test expression and jump depending on the result of
** the test and on Invert. The function returns one of the TESTEXPR_xx codes
** defined above. If the jump is always true, a warning is output.
** defined above.
*/
unsigned TestInParens (unsigned Label, int Invert);
/* Evaluate a boolean test expression in parenthesis and jump depending on
** the result of the test * and on Invert. The function returns one of the
** TESTEXPR_xx codes defined above. If the jump is always true, a warning is
** output.
** TESTEXPR_xx codes defined above.
*/

View File

@@ -152,6 +152,36 @@ $(WORKDIR)/bug2655.$1.$2.prg: bug2655.c $(ISEQUAL) | $(WORKDIR)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/bug2655.$1.$2.out
$(ISEQUAL) $(WORKDIR)/bug2655.$1.$2.out bug2655.ref
$(WORKDIR)/flow-do-01.$1.$2.prg: flow-do-01.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-do-01.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-do-01.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-do-01.$1.$2.out flow-do-01.ref
$(WORKDIR)/flow-if-01.$1.$2.prg: flow-if-01.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-if-01.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-if-01.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-if-01.$1.$2.out flow-if-01.ref
$(WORKDIR)/flow-if-02.$1.$2.prg: flow-if-02.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-if-02.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-if-02.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-if-02.$1.$2.out flow-if-02.ref
$(WORKDIR)/flow-switch-01.$1.$2.prg: flow-switch-01.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-switch-01.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-switch-01.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-switch-01.$1.$2.out flow-switch-01.ref
$(WORKDIR)/flow-while-01.$1.$2.prg: flow-while-01.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-while-01.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-while-01.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-while-01.$1.$2.out flow-while-01.ref
$(WORKDIR)/flow-while-02.$1.$2.prg: flow-while-02.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/flow-while-02.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLOUT) 2>$(WORKDIR)/flow-while-02.$1.$2.out
$(ISEQUAL) $(WORKDIR)/flow-while-02.$1.$2.out flow-while-02.ref
$(WORKDIR)/limits.$1.$2.prg: limits.c $(ISEQUAL) | $(WORKDIR)
$(if $(QUIET),echo misc/limits.$1.$2.prg)
$(CC65) -t sim$2 -$1 -o $$(@:.prg=.s) $$< $(NULLOUT) $(CATERR)

View File

@@ -1,2 +1,2 @@
bug2655.c:5: Warning: Unreachable code
bug2655.c:12: Warning: Unreachable code
bug2655.c:6: Warning: Unreachable code
bug2655.c:13: Warning: Unreachable code

87
test/misc/flow-do-01.c Normal file
View File

@@ -0,0 +1,87 @@
int a;
static int f1(void)
{
do {
return a;
} while (a == 2);
/* Unreachable */
a = 2;
return a;
}
static int f2(void)
{
do {
return a;
} while (1);
/* Unreachable */
a = 2;
return a;
}
static int f3(void)
{
do {
return a;
} while (0);
/* Unreachable */
a = 2;
return a;
}
static int f4(void)
{
do {
continue; /* Turns do/while into an endless loop */
} while (0);
/* Unreachable */
a = 2;
return a;
}
static int f5(void)
{
do {
if (a == 2) {
break;
}
return a;
} while (0);
/* Reachable */
a = 2;
return a;
}
static int f6(void)
{
do {
if (a == 2) {
break;
}
continue;
} while (0);
/* Reachable */
a = 2;
return a;
}
static int f7(void)
{
do {
if (a == 2) {
return a;
} else {
continue;
}
} while (1);
/* Unreachable */
a = 2;
return a;
}
int main(void)
{
return f1() + f2() + f3() + f4() + f5() + f6() + f7();
}

5
test/misc/flow-do-01.ref Normal file
View File

@@ -0,0 +1,5 @@
flow-do-01.c:9: Warning: Unreachable code
flow-do-01.c:19: Warning: Unreachable code
flow-do-01.c:29: Warning: Unreachable code
flow-do-01.c:39: Warning: Unreachable code
flow-do-01.c:79: Warning: Unreachable code

62
test/misc/flow-if-01.c Normal file
View File

@@ -0,0 +1,62 @@
int a, b;
static int f1(void)
{
if (a == 1) {
return 1;
}
/* Reachable */
a = 2;
return a;
}
static int f2(void)
{
if (a == 1) {
a = 2;
} else {
return 1;
}
/* Reachable */
return a;
}
static int f3(void)
{
if (a == 1) {
return 1;
} else {
a = 2;
}
/* Reachable */
return a;
}
static int f4(void)
{
if (a == 1) {
return 1;
} else {
return 0;
}
/* Unreachable */
a = 2;
}
static int f5(void)
{
if (1) {
return 1;
} else {
/* Unreachable */
return 0;
}
/* Unreachable */
a = 2;
}
int main(void)
{
return f1() + f2() + f3() + f4() + f5();
}

3
test/misc/flow-if-01.ref Normal file
View File

@@ -0,0 +1,3 @@
flow-if-01.c:43: Warning: Unreachable code
flow-if-01.c:52: Warning: Unreachable code
flow-if-01.c:55: Warning: Unreachable code

49
test/misc/flow-if-02.c Normal file
View File

@@ -0,0 +1,49 @@
int a, b;
static int f1(void)
{
if (0) {
/* Unreachable but no warning */
} else {
a = 2;
}
return a;
}
static int f2(void)
{
if (0) {
/* Unreachable */
a = 1;
} else {
a = 2;
}
return a;
}
static int f3(void)
{
if (1) {
a = 2;
} else {
/* Unreachable but no warning */
}
return a;
}
static int f4(void)
{
if (1) {
a = 2;
} else {
/* Unreachable */
a = 1;
}
return a;
}
int main(void)
{
return f1() + f2() + f3() + f4();
}

2
test/misc/flow-if-02.ref Normal file
View File

@@ -0,0 +1,2 @@
flow-if-02.c:17: Warning: Unreachable code
flow-if-02.c:40: Warning: Unreachable code

View File

@@ -0,0 +1,68 @@
int a;
static int f1(void)
{
switch (a) {
/* Unreachable */
a = 3;
case 1:
a = 2;
break;
case 2:
a = 1;
break;
default:
a = 0;
break;
}
/* Reachable */
return a;
}
static int f2(void)
{
switch (a) {
/* Reachable */
L: a = 3;
case 1:
goto L;
case 2:
a = 1;
break;
default:
a = 0;
break;
}
/* Reachable */
return a;
}
static int f3(void)
{
switch (a) {
case 1: return a;
case 2: return a+1;
default: return a+2;
}
/* Unreachable but no warning */
return a;
}
static int f4(void)
{
switch (a) {
/* No warning */
do {
case 1: ++a; continue;
case 2: return a+1;
default: return a+2;
} while (1);
}
/* Unreachable but no warning */
return a;
}
int main(void)
{
return f1() + f2() + f3() + f4();
}

View File

@@ -0,0 +1 @@
flow-switch-01.c:7: Warning: Unreachable code

70
test/misc/flow-while-01.c Normal file
View File

@@ -0,0 +1,70 @@
int a;
static int f1(void)
{
while (1) {
++a;
}
/* Unreachable */
a = 2;
return a;
}
static int f2(void)
{
while (1) {
++a;
if (a == 5) break;
}
/* Reachable */
a = 2;
return a;
}
static int f3(void)
{
while (1) {
++a;
return a;
}
/* Unreachable */
a = 2;
}
static int f4(void)
{
while (0) {
/* Unreachable */
++a;
return a;
}
/* Reachable */
a = 2;
return 0;
}
static int f5(void)
{
while (1) {
++a;
if (a == 4) goto L;
return a;
}
/* Reachable via L */
L: a = 2;
}
static int f6(void)
{
while (0) {
/* Unreachable but no warning */
}
a = 2;
return a;
}
int main(void)
{
return f1() + f2() + f3() + f4() + f5() + f6();
}

View File

@@ -0,0 +1,3 @@
flow-while-01.c:9: Warning: Unreachable code
flow-while-01.c:31: Warning: Unreachable code
flow-while-01.c:38: Warning: Unreachable code

74
test/misc/flow-while-02.c Normal file
View File

@@ -0,0 +1,74 @@
int a;
static int f1(void)
{
while (1) {
while (0) {
/* Unreachable */
++a;
}
}
/* Unreachable */
a = 2;
return a;
}
static int f2(void)
{
while (1) {
do {
return a;
} while (0);
/* Unreachable */
break;
}
/* Unreachable but no warning */
a = 2;
return a;
}
static int f3(void)
{
do {
while (1) {
break;
}
} while (1);
/* Unreachable */
a = 2;
return a;
}
static int f4(void)
{
do {
while (1) {
return a;
}
} while (0);
/* Unreachable */
a = 2;
return a;
}
static int f5(void)
{
do {
do {
if (a == 2) {
return a;
} else {
continue;
}
} while (0);
} while (0);
/* Unreachable */
a = 2;
return a;
}
int main(void)
{
return f1()/ + f2() + f3() + f4() + f5();
}

View File

@@ -0,0 +1,6 @@
flow-while-02.c:8: Warning: Unreachable code
flow-while-02.c:12: Warning: Unreachable code
flow-while-02.c:23: Warning: Unreachable code
flow-while-02.c:38: Warning: Unreachable code
flow-while-02.c:50: Warning: Unreachable code
flow-while-02.c:66: Warning: Unreachable code

View File

@@ -1,4 +1,9 @@
goto.c:3: Warning: Unreachable code
goto.c:8: Warning: Goto at line 8 to label start jumps into a block with initialization of an object that has automatic storage duration
goto.c:17: Warning: Unreachable code
goto.c:38: Warning: Unreachable code
goto.c:59: Warning: Unreachable code
goto.c:80: Warning: Unreachable code
goto.c:97: Warning: Variable 'a' is defined but never used
goto.c:117: Warning: Variable 'a' is defined but never used
goto.c:137: Warning: Variable 'a' is defined but never used
@@ -9,6 +14,7 @@ goto.c:159: Warning: Goto at line 86 to label l8 jumps into a block with initial
goto.c:159: Warning: Goto at line 106 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:159: Warning: Goto at line 126 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:159: Warning: Goto at line 146 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:161: Warning: Unreachable code
goto.c:180: Warning: Goto at line 24 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:180: Warning: Goto at line 45 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:180: Warning: Goto at line 66 to label l9 jumps into a block with initialization of an object that has automatic storage duration
@@ -17,6 +23,7 @@ goto.c:180: Warning: Goto at line 107 to label l9 jumps into a block with initia
goto.c:180: Warning: Goto at line 127 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:180: Warning: Goto at line 147 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:180: Warning: Goto at line 168 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:182: Warning: Unreachable code
goto.c:201: Warning: Goto at line 25 to label la jumps into a block with initialization of an object that has automatic storage duration
goto.c:201: Warning: Goto at line 46 to label la jumps into a block with initialization of an object that has automatic storage duration
goto.c:201: Warning: Goto at line 67 to label la jumps into a block with initialization of an object that has automatic storage duration
@@ -26,6 +33,7 @@ goto.c:201: Warning: Goto at line 128 to label la jumps into a block with initia
goto.c:201: Warning: Goto at line 148 to label la jumps into a block with initialization of an object that has automatic storage duration
goto.c:201: Warning: Goto at line 169 to label la jumps into a block with initialization of an object that has automatic storage duration
goto.c:201: Warning: Goto at line 190 to label la jumps into a block with initialization of an object that has automatic storage duration
goto.c:203: Warning: Unreachable code
goto.c:221: Warning: Goto at line 26 to label lb jumps into a block with initialization of an object that has automatic storage duration
goto.c:221: Warning: Goto at line 47 to label lb jumps into a block with initialization of an object that has automatic storage duration
goto.c:221: Warning: Goto at line 68 to label lb jumps into a block with initialization of an object that has automatic storage duration
@@ -57,6 +65,7 @@ goto.c:263: Warning: Goto at line 193 to label ld jumps into a block with initia
goto.c:263: Warning: Goto at line 214 to label ld jumps into a block with initialization of an object that has automatic storage duration
goto.c:263: Warning: Goto at line 234 to label ld jumps into a block with initialization of an object that has automatic storage duration
goto.c:263: Warning: Goto at line 254 to label ld jumps into a block with initialization of an object that has automatic storage duration
goto.c:265: Warning: Unreachable code
goto.c:271: Warning: Goto at line 271 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:272: Warning: Goto at line 272 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:273: Warning: Goto at line 273 to label la jumps into a block with initialization of an object that has automatic storage duration
@@ -75,6 +84,7 @@ goto.c:284: Warning: Goto at line 215 to label le jumps into a block with initia
goto.c:284: Warning: Goto at line 235 to label le jumps into a block with initialization of an object that has automatic storage duration
goto.c:284: Warning: Goto at line 255 to label le jumps into a block with initialization of an object that has automatic storage duration
goto.c:284: Warning: Goto at line 277 to label le jumps into a block with initialization of an object that has automatic storage duration
goto.c:286: Warning: Unreachable code
goto.c:292: Warning: Goto at line 292 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:293: Warning: Goto at line 293 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:294: Warning: Goto at line 294 to label la jumps into a block with initialization of an object that has automatic storage duration
@@ -94,6 +104,7 @@ goto.c:305: Warning: Goto at line 236 to label lf jumps into a block with initia
goto.c:305: Warning: Goto at line 256 to label lf jumps into a block with initialization of an object that has automatic storage duration
goto.c:305: Warning: Goto at line 278 to label lf jumps into a block with initialization of an object that has automatic storage duration
goto.c:305: Warning: Goto at line 299 to label lf jumps into a block with initialization of an object that has automatic storage duration
goto.c:307: Warning: Unreachable code
goto.c:313: Warning: Goto at line 313 to label l8 jumps into a block with initialization of an object that has automatic storage duration
goto.c:314: Warning: Goto at line 314 to label l9 jumps into a block with initialization of an object that has automatic storage duration
goto.c:315: Warning: Goto at line 315 to label la jumps into a block with initialization of an object that has automatic storage duration