Merge remote-tracking branch 'remotes/Daniel-Cortez/loop-diagnostics' into dev
This commit is contained in:
commit
6781afeada
@ -229,6 +229,16 @@ typedef struct s_symbol {
|
||||
#define uRETNONE 0x010
|
||||
/* uASSIGNED indicates that a value assigned to the variable is not used yet */
|
||||
#define uASSIGNED 0x080
|
||||
/* uLOOPVAR is set when a variable is read inside of a loop condition. This is
|
||||
* used to detect situations when a variable is used in a loop condition, but
|
||||
* not modified inside of a loop body. */
|
||||
#define uLOOPVAR 0x1000
|
||||
/* uNOLOOPVAR is set when a variable is
|
||||
* * modified inside of a loop condition before being read, or
|
||||
* * used in an enclosing loop and should be excluded from checks in an inner loop,
|
||||
* so the compiler would know it shouldn't set the uLOOPVAR flag when the variable
|
||||
* is read inside a loop condition */
|
||||
#define uNOLOOPVAR 0x2000
|
||||
|
||||
#define flagDEPRECATED 0x01 /* symbol is deprecated (avoid use) */
|
||||
#define flagNAKED 0x10 /* function is naked */
|
||||
@ -300,15 +310,17 @@ typedef struct s_valuepair {
|
||||
long second;
|
||||
} valuepair;
|
||||
|
||||
/* struct "assigninfo" is used to synchronize the status of assignments that
|
||||
* were made in multiple "if" and "switch" branches, so the compiler could
|
||||
* detect unused assignments in all of those branches, not only the last one */
|
||||
/* struct "symstate" is used to:
|
||||
* * synchronize the status of assignments between all "if" branches or "switch"
|
||||
* cases, so the compiler could detect unused assignments in all of those
|
||||
* branches/cases, not only in the last one;
|
||||
* * back up the "uNOLOOPVAR" flag when scanning for variables that were used
|
||||
* in a loop exit condition, but weren't modified inside the loop body */
|
||||
typedef struct s_assigninfo {
|
||||
int unused; /* true if the variable has an unused value assigned to it
|
||||
* in one of the branches" */
|
||||
int lnumber; /* line number of the first unused assignment made in one of
|
||||
* the branches (used for error messages) */
|
||||
} assigninfo;
|
||||
short usage; /* usage flags to memoize (currently only uASSIGNED) */
|
||||
} symstate;
|
||||
|
||||
/* macros for code generation */
|
||||
#define opcodes(n) ((n)*sizeof(cell)) /* opcode size */
|
||||
@ -733,8 +745,8 @@ SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom);
|
||||
SC_FUNC void markusage(symbol *sym,int usage);
|
||||
SC_FUNC void markinitialized(symbol *sym,int assignment);
|
||||
SC_FUNC void clearassignments(int fromlevel);
|
||||
SC_FUNC void memoizeassignments(int fromlevel,assigninfo **assignments);
|
||||
SC_FUNC void restoreassignments(int fromlevel,assigninfo *assignments);
|
||||
SC_FUNC void memoizeassignments(int fromlevel,symstate **assignments);
|
||||
SC_FUNC void restoreassignments(int fromlevel,symstate *assignments);
|
||||
SC_FUNC void rename_symbol(symbol *sym,const char *newname);
|
||||
SC_FUNC symbol *findglb(const char *name,int filter);
|
||||
SC_FUNC symbol *findloc(const char *name);
|
||||
@ -1009,6 +1021,8 @@ SC_VDECL int pc_retheap; /* heap space (in bytes) to be manually freed when
|
||||
SC_VDECL int pc_nestlevel; /* number of active (open) compound statements */
|
||||
SC_VDECL unsigned int pc_attributes;/* currently set attribute flags (for the "__pragma" operator) */
|
||||
SC_VDECL int pc_ispackedstr; /* true if the last tokenized string is packed */
|
||||
SC_VDECL int pc_loopcond; /* equals to 'tFOR', 'tWHILE' or 'tDO' if the current expression is a loop condition, zero otherwise */
|
||||
SC_VDECL int pc_numloopvars; /* number of variables used inside a loop condition */
|
||||
|
||||
SC_VDECL char *sc_tokens[];
|
||||
|
||||
|
@ -116,6 +116,8 @@ static void make_report(symbol *root,FILE *log,char *sourcefile);
|
||||
static void reduce_referrers(symbol *root);
|
||||
static long max_stacksize(symbol *root,int *recursion);
|
||||
static int testsymbols(symbol *root,int level,int testlabs,int testconst);
|
||||
static void scanloopvariables(symstate **loopvars,int dowhile);
|
||||
static void testloopvariables(symstate *loopvars,int dowhile,int line);
|
||||
static void destructsymbols(symbol *root,int level);
|
||||
static constvalue *find_constval_byval(constvalue_root *table,cell val);
|
||||
static symbol *fetchlab(char *name);
|
||||
@ -919,6 +921,7 @@ static void resetglobals(void)
|
||||
pc_naked=FALSE;
|
||||
pc_retexpr=FALSE;
|
||||
pc_attributes=0;
|
||||
pc_loopcond=0;
|
||||
emit_flags=0;
|
||||
emit_stgbuf_idx=-1;
|
||||
}
|
||||
@ -5173,6 +5176,112 @@ static int testsymbols(symbol *root,int level,int testlabs,int testconst)
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void scanloopvariables(symstate **loopvars,int dowhile)
|
||||
{
|
||||
symbol *start,*sym;
|
||||
int num;
|
||||
|
||||
/* error messages are only printed on the "writing" pass,
|
||||
* so if we are not writing yet, then we have a quick exit */
|
||||
if (sc_status!=statWRITE)
|
||||
return;
|
||||
|
||||
/* if there's no enclosing loop (only one active loop entry, which is the
|
||||
* current loop), and the current loop is not 'do-while', then we don't need
|
||||
* to memoize usage flags for local variables, so we have an early exit */
|
||||
if (wqptr-wqSIZE==wq && !dowhile)
|
||||
return;
|
||||
|
||||
/* skip labels */
|
||||
start=&loctab;
|
||||
while ((start=start->next)!=NULL && start->ident==iLABEL)
|
||||
/* nothing */;
|
||||
/* if there are no other local symbols, we have an early exit */
|
||||
if (start==NULL)
|
||||
return;
|
||||
|
||||
/* count the number of local symbols */
|
||||
for (num=0,sym=start; sym!=NULL; num++,sym=sym->next)
|
||||
/* nothing */;
|
||||
|
||||
assert(*loopvars==NULL);
|
||||
assert(num!=0);
|
||||
*loopvars=(symstate *)calloc((size_t)num,sizeof(symstate));
|
||||
if (*loopvars==NULL)
|
||||
error(103); /* insufficient memory */
|
||||
|
||||
for (num=0,sym=start; sym!=NULL; num++,sym=sym->next) {
|
||||
/* If the variable already has the uLOOPVAR flag set (from being used
|
||||
* in an enclosing loop), we have to set the uNOLOOPVAR to exclude it
|
||||
* from checks in the current loop, ... */
|
||||
if ((sym->ident==iVARIABLE || sym->ident==iREFERENCE)
|
||||
&& (dowhile || (sym->usage & uLOOPVAR)!=0)) {
|
||||
/* ... but it might be already set from an enclosing loop as well, so we
|
||||
* have to temporarily store it in "loopvars[num]" first. Also, if this is
|
||||
* a 'do-while' loop, we need to memoize and unset the 'uWRITTEN' flag, so
|
||||
* later when analyzing the loop condition (which comes after the loop
|
||||
* body) we'll be able to determine if the variable was modified inside
|
||||
* the loop body by checking if the 'uWRITTEN' flag is set. */
|
||||
(*loopvars)[num].usage |= (sym->usage & (uNOLOOPVAR | uWRITTEN));
|
||||
sym->usage &= ~uWRITTEN;
|
||||
if (wqptr-wqSIZE!=wq)
|
||||
sym->usage |= uNOLOOPVAR;
|
||||
} /* if */
|
||||
} /* if */
|
||||
}
|
||||
|
||||
static void testloopvariables(symstate *loopvars,int dowhile,int line)
|
||||
{
|
||||
symbol *start,*sym;
|
||||
int num,warnnum=0;
|
||||
|
||||
/* the error messages are only printed on the "writing" pass,
|
||||
* so if we are not writing yet, then we have a quick exit */
|
||||
if (sc_status!=statWRITE)
|
||||
return;
|
||||
|
||||
/* skip labels */
|
||||
start=&loctab;
|
||||
while ((start=start->next)!=NULL && start->ident==iLABEL)
|
||||
/* nothing */;
|
||||
|
||||
/* decrement pc_numloopvars by 1 for each variable that wasn't modified
|
||||
* inside the loop body; if pc_numloopvars gets zeroed after this, it would
|
||||
* mean none of the variables used inside the loop condition were modified */
|
||||
if (pc_numloopvars!=0) {
|
||||
warnnum=(pc_numloopvars==1) ? 250 : 251;
|
||||
for (sym=start; sym!=NULL; sym=sym->next)
|
||||
if ((sym->ident==iVARIABLE || sym->ident==iREFERENCE)
|
||||
&& (sym->usage & (uLOOPVAR | uNOLOOPVAR))==uLOOPVAR
|
||||
&& (!dowhile || (sym->usage & uWRITTEN)==0))
|
||||
pc_numloopvars--;
|
||||
if (pc_numloopvars==0 && warnnum==251) {
|
||||
errorset(sSETPOS,line);
|
||||
error(251); /* none of the variables used in loop condition are modified in loop body */
|
||||
errorset(sSETPOS,-1);
|
||||
} /* if */
|
||||
} /* if */
|
||||
|
||||
for (num=0,sym=start; sym!=NULL; num++,sym=sym->next) {
|
||||
if (sym->ident==iVARIABLE || sym->ident==iREFERENCE) {
|
||||
if ((sym->usage & (uLOOPVAR | uNOLOOPVAR))==uLOOPVAR) {
|
||||
sym->usage &= ~uLOOPVAR;
|
||||
/* warn only if none of the variables used inside the loop condition
|
||||
* were modified inside the loop body */
|
||||
if (pc_numloopvars==0 && warnnum==250) {
|
||||
errorset(sSETPOS,line);
|
||||
error(250,sym->name); /* variable used in loop condition not modified in loop body */
|
||||
errorset(sSETPOS,-1);
|
||||
} /* if */
|
||||
} /* if */
|
||||
sym->usage &= ~uNOLOOPVAR;
|
||||
if (loopvars!=NULL)
|
||||
sym->usage |= loopvars[num].usage;
|
||||
} /* if */
|
||||
} /* for */
|
||||
free(loopvars);
|
||||
}
|
||||
|
||||
static cell calc_array_datasize(symbol *sym, cell *offset)
|
||||
{
|
||||
cell length;
|
||||
@ -5883,7 +5992,7 @@ static int doif(void)
|
||||
int ifindent;
|
||||
int lastst_true;
|
||||
int returnst=tIF;
|
||||
assigninfo *assignments=NULL;
|
||||
symstate *assignments=NULL;
|
||||
|
||||
lastst=0; /* reset the last statement */
|
||||
ifindent=stmtindent; /* save the indent of the "if" instruction */
|
||||
@ -5924,9 +6033,13 @@ static int doif(void)
|
||||
static int dowhile(void)
|
||||
{
|
||||
int wq[wqSIZE]; /* allocate local queue */
|
||||
int save_endlessloop,retcode;
|
||||
int save_endlessloop,save_numloopvars,retcode;
|
||||
int loopline=fline;
|
||||
symstate *loopvars=NULL;
|
||||
|
||||
save_endlessloop=endlessloop;
|
||||
save_numloopvars=pc_numloopvars;
|
||||
pc_numloopvars=0;
|
||||
addwhile(wq); /* add entry to queue for "break" */
|
||||
setlabel(wq[wqLOOP]); /* loop label */
|
||||
/* The debugger uses the "break" opcode to be able to "break" out of
|
||||
@ -5934,18 +6047,23 @@ static int dowhile(void)
|
||||
* tiniest loop, set it below the top of the loop
|
||||
*/
|
||||
setline(TRUE);
|
||||
scanloopvariables(&loopvars,FALSE);
|
||||
pc_nestlevel++; /* temporarily increase the "compound statement" nesting level,
|
||||
* so any assignments made inside the loop control expression
|
||||
* could be cleaned up later */
|
||||
pc_loopcond=tWHILE;
|
||||
endlessloop=test(wq[wqEXIT],TEST_DO,FALSE);/* branch to wq[wqEXIT] if false */
|
||||
pc_loopcond=0;
|
||||
pc_nestlevel--;
|
||||
statement(NULL,FALSE); /* if so, do a statement */
|
||||
clearassignments(pc_nestlevel+1);
|
||||
testloopvariables(loopvars,FALSE,loopline);
|
||||
jumplabel(wq[wqLOOP]); /* and loop to "while" start */
|
||||
setlabel(wq[wqEXIT]); /* exit label */
|
||||
delwhile(); /* delete queue entry */
|
||||
|
||||
retcode=endlessloop ? tENDLESS : tWHILE;
|
||||
pc_numloopvars=save_numloopvars;
|
||||
endlessloop=save_endlessloop;
|
||||
return retcode;
|
||||
}
|
||||
@ -5957,12 +6075,17 @@ static int dowhile(void)
|
||||
static int dodo(void)
|
||||
{
|
||||
int wq[wqSIZE],top;
|
||||
int save_endlessloop,retcode;
|
||||
int save_endlessloop,save_numloopvars,retcode;
|
||||
int loopline=fline;
|
||||
symstate *loopvars=NULL;
|
||||
|
||||
save_endlessloop=endlessloop;
|
||||
save_numloopvars=pc_numloopvars;
|
||||
pc_numloopvars=0;
|
||||
addwhile(wq); /* see "dowhile" for more info */
|
||||
top=getlabel(); /* make a label first */
|
||||
setlabel(top); /* loop label */
|
||||
scanloopvariables(&loopvars,TRUE);
|
||||
statement(NULL,FALSE);
|
||||
needtoken(tWHILE);
|
||||
setlabel(wq[wqLOOP]); /* "continue" always jumps to WQLOOP. */
|
||||
@ -5970,15 +6093,19 @@ static int dodo(void)
|
||||
pc_nestlevel++; /* temporarily increase the "compound statement" nesting level,
|
||||
* so any assignments made inside the loop control expression
|
||||
* could be cleaned up later */
|
||||
pc_loopcond=tDO;
|
||||
endlessloop=test(wq[wqEXIT],TEST_OPT,FALSE);
|
||||
pc_loopcond=0;
|
||||
pc_nestlevel--;
|
||||
clearassignments(pc_nestlevel+1);
|
||||
testloopvariables(loopvars,TRUE,loopline);
|
||||
jumplabel(top);
|
||||
setlabel(wq[wqEXIT]);
|
||||
delwhile();
|
||||
needtoken(tTERM);
|
||||
|
||||
retcode=endlessloop ? tENDLESS : tDO;
|
||||
pc_numloopvars=save_numloopvars;
|
||||
endlessloop=save_endlessloop;
|
||||
return retcode;
|
||||
}
|
||||
@ -5987,13 +6114,17 @@ static int dofor(void)
|
||||
{
|
||||
int wq[wqSIZE],skiplab;
|
||||
cell save_decl;
|
||||
int save_nestlevel,save_endlessloop;
|
||||
int save_nestlevel,save_endlessloop,save_numloopvars;
|
||||
int index,endtok;
|
||||
int *ptr;
|
||||
int loopline=fline;
|
||||
symstate *loopvars=NULL;
|
||||
|
||||
save_decl=declared;
|
||||
save_nestlevel=pc_nestlevel;
|
||||
save_endlessloop=endlessloop;
|
||||
save_numloopvars=pc_numloopvars;
|
||||
pc_numloopvars=0;
|
||||
|
||||
addwhile(wq);
|
||||
skiplab=getlabel();
|
||||
@ -6026,6 +6157,7 @@ static int dofor(void)
|
||||
jumplabel(skiplab); /* skip expression 3 1st time */
|
||||
setlabel(wq[wqLOOP]); /* "continue" goes to this label: expr3 */
|
||||
setline(TRUE);
|
||||
scanloopvariables(&loopvars,FALSE);
|
||||
/* Expressions 2 and 3 are reversed in the generated code: expression 3
|
||||
* precedes expression 2. When parsing, the code is buffered and marks for
|
||||
* the start of each expression are insterted in the buffer.
|
||||
@ -6040,7 +6172,9 @@ static int dofor(void)
|
||||
if (matchtoken(';')) {
|
||||
endlessloop=1;
|
||||
} else {
|
||||
pc_loopcond=tFOR;
|
||||
endlessloop=test(wq[wqEXIT],TEST_PLAIN,FALSE);/* expression 2 (jump to wq[wqEXIT] if false) */
|
||||
pc_loopcond=0;
|
||||
needtoken(';');
|
||||
} /* if */
|
||||
stgmark((char)(sEXPRSTART+1)); /* mark start of 3th expression in stage */
|
||||
@ -6053,6 +6187,7 @@ static int dofor(void)
|
||||
stgset(FALSE); /* stop staging */
|
||||
statement(NULL,FALSE);
|
||||
clearassignments(save_nestlevel+1);
|
||||
testloopvariables(loopvars,FALSE,loopline);
|
||||
jumplabel(wq[wqLOOP]);
|
||||
setlabel(wq[wqEXIT]);
|
||||
delwhile();
|
||||
@ -6071,6 +6206,7 @@ static int dofor(void)
|
||||
pc_nestlevel=save_nestlevel; /* reset 'compound statement' nesting level */
|
||||
|
||||
index=endlessloop ? tENDLESS : tFOR;
|
||||
pc_numloopvars=save_numloopvars;
|
||||
endlessloop=save_endlessloop;
|
||||
return index;
|
||||
}
|
||||
@ -6103,7 +6239,7 @@ static int doswitch(void)
|
||||
constvalue_root caselist = { NULL, NULL}; /* case list starts empty */
|
||||
constvalue *cse,*csp,*newval;
|
||||
char labelname[sNAMEMAX+1];
|
||||
assigninfo *assignments=NULL;
|
||||
symstate *assignments=NULL;
|
||||
|
||||
endtok= matchtoken('(') ? ')' : tDO;
|
||||
ident=doexpr(TRUE,FALSE,FALSE,FALSE,&swtag,NULL,TRUE,NULL); /* evaluate switch expression */
|
||||
|
@ -55,6 +55,7 @@ static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int a
|
||||
static void substallpatterns(unsigned char *line,int buffersize);
|
||||
static int match(char *st,int end);
|
||||
static int alpha(char c);
|
||||
static void markloopvariable(symbol *sym,int usage);
|
||||
|
||||
#define SKIPMODE 1 /* bit field in "#if" stack */
|
||||
#define PARSEMODE 2 /* bit field in "#if" stack */
|
||||
@ -3276,6 +3277,8 @@ SC_FUNC void markusage(symbol *sym,int usage)
|
||||
sym->lnumber=fline;
|
||||
if ((usage & uREAD)!=0 && (sym->ident==iVARIABLE || sym->ident==iREFERENCE))
|
||||
sym->usage &= ~uASSIGNED;
|
||||
if ((usage & (uREAD | uWRITTEN))!=0 && (sym->ident==iVARIABLE || sym->ident==iREFERENCE))
|
||||
markloopvariable(sym,usage);
|
||||
/* check if (global) reference must be added to the symbol */
|
||||
if ((usage & (uREAD | uWRITTEN))!=0) {
|
||||
/* only do this for global symbols */
|
||||
@ -3306,7 +3309,7 @@ SC_FUNC void clearassignments(int fromlevel)
|
||||
{
|
||||
symbol *sym;
|
||||
|
||||
/* the error messages are only printed on the "writing" pass,
|
||||
/* error messages are only printed on the "writing" pass,
|
||||
* so if we are not writing yet, then we have a quick exit */
|
||||
if (sc_status!=statWRITE)
|
||||
return;
|
||||
@ -3318,12 +3321,12 @@ SC_FUNC void clearassignments(int fromlevel)
|
||||
}
|
||||
|
||||
/* memoizes all assignments done on the specified compound level and higher */
|
||||
SC_FUNC void memoizeassignments(int fromlevel,assigninfo **assignments)
|
||||
SC_FUNC void memoizeassignments(int fromlevel,symstate **assignments)
|
||||
{
|
||||
symbol *sym;
|
||||
int num;
|
||||
|
||||
/* the error messages are only printed on the "writing" pass,
|
||||
/* error messages are only printed on the "writing" pass,
|
||||
* so if we are not writing yet, then we have a quick exit */
|
||||
if (sc_status!=statWRITE)
|
||||
return;
|
||||
@ -3338,7 +3341,7 @@ SC_FUNC void memoizeassignments(int fromlevel,assigninfo **assignments)
|
||||
/* if there are no variables, then we have an early exit */
|
||||
if (num==0)
|
||||
return;
|
||||
*assignments=(assigninfo *)calloc((size_t)num,sizeof(assigninfo));
|
||||
*assignments=(symstate *)calloc((size_t)num,sizeof(symstate));
|
||||
if (*assignments==NULL)
|
||||
error(103); /* insufficient memory */
|
||||
} /* if */
|
||||
@ -3353,16 +3356,17 @@ SC_FUNC void memoizeassignments(int fromlevel,assigninfo **assignments)
|
||||
sym->usage &= ~uASSIGNED;
|
||||
/* memoize the assignment only if there was no other unused assignment
|
||||
* in any other "if" or "switch" branch */
|
||||
if ((*assignments)[num].unused==FALSE) {
|
||||
(*assignments)[num].unused=TRUE;
|
||||
assert_static(sizeof(sym->usage)==sizeof((*assignments)->usage));
|
||||
if (((*assignments)[num].usage & uASSIGNED)==0) {
|
||||
(*assignments)[num].lnumber=sym->lnumber;
|
||||
(*assignments)[num].usage |= uASSIGNED;
|
||||
} /* if */
|
||||
} /* if */
|
||||
} /* for */
|
||||
}
|
||||
|
||||
/* restores all memoized assignments */
|
||||
SC_FUNC void restoreassignments(int fromlevel,assigninfo *assignments)
|
||||
SC_FUNC void restoreassignments(int fromlevel,symstate *assignments)
|
||||
{
|
||||
symbol *sym;
|
||||
int num;
|
||||
@ -3370,7 +3374,7 @@ SC_FUNC void restoreassignments(int fromlevel,assigninfo *assignments)
|
||||
sym=&loctab;
|
||||
while ((sym=sym->next)!=NULL && sym->ident==iLABEL) {} /* skip labels */
|
||||
for (num=0; sym!=NULL; num++,sym=sym->next) {
|
||||
if (assignments!=NULL && assignments[num].unused) {
|
||||
if (assignments!=NULL && (assignments[num].usage & uASSIGNED)!=0) {
|
||||
sym->usage |= uASSIGNED;
|
||||
sym->lnumber=assignments[num].lnumber;
|
||||
} /* if */
|
||||
@ -3382,6 +3386,37 @@ SC_FUNC void restoreassignments(int fromlevel,assigninfo *assignments)
|
||||
free(assignments);
|
||||
}
|
||||
|
||||
static void markloopvariable(symbol *sym,int usage)
|
||||
{
|
||||
if (sc_status!=statWRITE)
|
||||
return;
|
||||
while (sym->parent!=NULL)
|
||||
sym=sym->parent;
|
||||
/* check if the variable used inside a loop condition */
|
||||
if (pc_loopcond!=0) {
|
||||
if (sym->vclass==sGLOBAL) {
|
||||
/* stop counting variables that were used in loop condition, otherwise
|
||||
* warnings 250 and 251 may be inaccurate (global variables can be
|
||||
* modified from another function(s) called from the loop body, and
|
||||
* currently there's no reasonable way to track this) */
|
||||
pc_loopcond=0;
|
||||
pc_numloopvars=0;
|
||||
} else if ((usage & uWRITTEN)!=0) {
|
||||
/* the symbol is being modified inside a loop condition before being read;
|
||||
* set the uNOLOOPVAR flag, so later we'll know we shouldn't mark the symbol
|
||||
* with the uLOOPVAR flag */
|
||||
sym->usage |= uNOLOOPVAR;
|
||||
pc_numloopvars++;
|
||||
} else if ((usage & uREAD)!=0 && (sym->usage & (uNOLOOPVAR | uLOOPVAR))==0) {
|
||||
sym->usage |= uLOOPVAR;
|
||||
pc_numloopvars++;
|
||||
} /* if */
|
||||
} /* if */
|
||||
/* unset the uLOOPVAR flag if the variable is being modified */
|
||||
if ((usage & uWRITTEN)!=0)
|
||||
sym->usage &= ~uLOOPVAR;
|
||||
}
|
||||
|
||||
|
||||
/* findglb
|
||||
*
|
||||
|
@ -1804,6 +1804,12 @@ restart:
|
||||
error(51); /* invalid subscript, must use [ ] */
|
||||
invsubscript=TRUE;
|
||||
} /* if */
|
||||
if (pc_loopcond!=0) {
|
||||
/* stop counting variables that were used in loop condition,
|
||||
* otherwise warnings 250 and 251 may be inaccurate */
|
||||
pc_loopcond=0;
|
||||
pc_numloopvars=0;
|
||||
} /* if */
|
||||
if (invsubscript) {
|
||||
if (sym!=NULL && sym->ident!=iFUNCTN)
|
||||
sym->usage |= uREAD; /* avoid the "symbol is never used" warning */
|
||||
@ -2229,7 +2235,13 @@ static int nesting=0;
|
||||
/* functions cannot be called at global scope */
|
||||
error(29); /* invalid expression, assumed zero */
|
||||
return;
|
||||
}
|
||||
} /* if */
|
||||
if (pc_loopcond!=0) {
|
||||
/* stop counting variables that were used in loop condition,
|
||||
* otherwise warnings 249 and 250 may be inaccurate */
|
||||
pc_loopcond=0;
|
||||
pc_numloopvars=0;
|
||||
} /* if */
|
||||
/* check whether this is a function that returns an array */
|
||||
symret=sym->child;
|
||||
assert(symret==NULL || symret->ident==iREFARRAY);
|
||||
@ -2424,9 +2436,9 @@ static int nesting=0;
|
||||
check_tagmismatch_multiple(arg[argidx].tags,arg[argidx].numtags,lval.tag,-1);
|
||||
if (lval.tag!=0)
|
||||
append_constval(&taglst,arg[argidx].name,lval.tag,0);
|
||||
argidx++; /* argument done */
|
||||
if (lval.sym!=NULL)
|
||||
if (lval.sym!=NULL && (arg[argidx].usage & uCONST)==0)
|
||||
markusage(lval.sym,uWRITTEN);
|
||||
argidx++; /* argument done */
|
||||
break;
|
||||
case iREFARRAY:
|
||||
if (lval.ident!=iARRAY && lval.ident!=iREFARRAY
|
||||
|
@ -206,7 +206,10 @@ static char *warnmsg[] = {
|
||||
/*245*/ "enum increment \"%s %d\" has no effect on zero value (symbol \"%s\")\n",
|
||||
/*246*/ "multiplication overflow in enum element declaration (symbol \"%s\")\n",
|
||||
/*247*/ "use of operator \"%s\" on %s\n",
|
||||
/*248*/ "possible misuse of comma operator\n"
|
||||
/*248*/ "possible misuse of comma operator\n",
|
||||
/*249*/ "",
|
||||
/*250*/ "variable \"%s\" used in loop condition not modified in loop body\n",
|
||||
/*251*/ "none of the variables used in loop condition are modified in loop body\n"
|
||||
};
|
||||
|
||||
static char *noticemsg[] = {
|
||||
|
@ -103,6 +103,8 @@ SC_VDEFINE int pc_retheap=0; /* heap space (in bytes) to be manua
|
||||
SC_VDEFINE int pc_nestlevel=0; /* number of active (open) compound statements */
|
||||
SC_VDEFINE unsigned int pc_attributes=0; /* currently set attribute flags (for the "__pragma" operator) */
|
||||
SC_VDEFINE int pc_ispackedstr=FALSE; /* true if the last tokenized string is packed */
|
||||
SC_VDEFINE int pc_loopcond=FALSE; /* true if the current expression is a loop condition */
|
||||
SC_VDEFINE int pc_numloopvars=0; /* number of variables used inside a loop condition */
|
||||
|
||||
SC_VDEFINE char *sc_tokens[] = {
|
||||
"*=", "/=", "%=", "+=", "-=", "<<=", ">>>=", ">>=", "&=", "^=", "|=",
|
||||
|
16
source/compiler/tests/warning_250_251.meta
Normal file
16
source/compiler/tests/warning_250_251.meta
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
'test_type': 'output_check',
|
||||
'errors': """
|
||||
warning_250_251.pwn(19) : warning 250: variable "n" used in loop condition not modified in loop body
|
||||
warning_250_251.pwn(20) : warning 250: variable "n" used in loop condition not modified in loop body
|
||||
warning_250_251.pwn(21) : warning 250: variable "i" used in loop condition not modified in loop body
|
||||
warning_250_251.pwn(42) : warning 250: variable "n" used in loop condition not modified in loop body
|
||||
warning_250_251.pwn(43) : warning 250: variable "n" used in loop condition not modified in loop body
|
||||
warning_250_251.pwn(44) : warning 250: variable "i" used in loop condition not modified in loop body
|
||||
warning_250_251.pwn(59) : warning 251: none of the variables used in loop condition are modified in loop body
|
||||
warning_250_251.pwn(60) : warning 251: none of the variables used in loop condition are modified in loop body
|
||||
warning_250_251.pwn(61) : warning 251: none of the variables used in loop condition are modified in loop body
|
||||
warning_250_251.pwn(122) : warning 250: variable "n" used in loop condition not modified in loop body
|
||||
warning_250_251.pwn(123) : warning 251: none of the variables used in loop condition are modified in loop body
|
||||
"""
|
||||
}
|
124
source/compiler/tests/warning_250_251.pwn
Normal file
124
source/compiler/tests/warning_250_251.pwn
Normal file
@ -0,0 +1,124 @@
|
||||
#include <core>
|
||||
#include <file>
|
||||
|
||||
new glbvar = 0;
|
||||
|
||||
stock UseVarByRef(&arg)
|
||||
return arg;
|
||||
|
||||
#pragma warning disable 238 // "meaningless combination of class specifiers (const reference)"
|
||||
stock UseVarByConstRef(const &arg)
|
||||
return arg;
|
||||
|
||||
main()
|
||||
{
|
||||
new n = 0, m = 10;
|
||||
static st = 0;
|
||||
|
||||
// Case 1: Variable is used inside a loop condition without being modified.
|
||||
while (n < 10) {} // warning 250: variable "n" used in loop condition not modified in loop body
|
||||
do {} while (n < 10); // warning 250: variable "n" used in loop condition not modified in loop body
|
||||
for (new i = 0, j = 0; i < 10; ++j) {} // warning 250: variable "i" used in loop condition not modified in loop body
|
||||
|
||||
// Case 2: Variable is used inside a loop condition and modified in the loop body.
|
||||
while (n != 0) { n++; }
|
||||
do { n++; } while (n < 10);
|
||||
for (new i = 0; i < 10; ) { i++; }
|
||||
|
||||
// Case 3: Variable is used inside a loop condition and modified in the
|
||||
// loop counter increment/decrement section.
|
||||
for (new i = 0; i < 10; i++) {}
|
||||
|
||||
// Case 4: Variable is used and modified inside a loop condition.
|
||||
while (n++ != 0) {}
|
||||
while (++n != 0) {}
|
||||
do {} while (n++ != 0);
|
||||
do {} while (++n != 0);
|
||||
for (new i = 0; i++ < 10; ) {}
|
||||
for (new i = 0; ++i < 10; ) {}
|
||||
|
||||
// Case 5: Same variable is used inside a loop condition more than once
|
||||
// and it's not modified.
|
||||
while (n == 0 || n < 10) {} // warning 250: variable "n" used in loop condition not modified in loop body
|
||||
do {} while (n == 0 || n < 10); // warning 250: variable "n" used in loop condition not modified in loop body
|
||||
for (new i = 0; i == 0 || i < 10; ) {} // warning 250: variable "i" used in loop condition not modified in loop body
|
||||
|
||||
// Case 6: Same variable is used inside a loop condition more than once,
|
||||
// but it's modified.
|
||||
while (n == 0 || n < 10) { n++; }
|
||||
do { n++; } while (n == 0 || n < 10);
|
||||
for (new i = 0; i == 0 || i < 10; i++) {}
|
||||
|
||||
// Case 7: Two variables are used inside a loop condition, both aren't modified.
|
||||
// Printing warning 250 for each unmodified variable wouldn't be productive, because:
|
||||
// 1. the user would be spammed with multiple warnings, which can be annoying;
|
||||
// 2. depending on the context, only one of the variables might be supposed
|
||||
// to be modified, but the compiler can't know which one exactly.
|
||||
// Solution: introduce a warning that simply says that none of the variables
|
||||
// were modified, and let the user decide which variable should be modified.
|
||||
while (n < m) {} // warning 251: none of the variables used in loop condition are modified in loop body
|
||||
do {} while (n < m); // warning 251: none of the variables used in loop condition are modified in loop body
|
||||
for (new i = 0; i < m; ) {} // warning 251: none of the variables used in loop condition are modified in loop body
|
||||
|
||||
// Case 7: Two variables are used in a loop condition, but one of them
|
||||
// is modified inside the loop body (or the loop counter increment/decrement
|
||||
// section of a "for" loop), and the other one is not modified.
|
||||
while (n < m) { ++n; }
|
||||
do { --m; } while (n < m);
|
||||
for (new i = 0; i < m; ) { i++; }
|
||||
for (new i = 0; i < m; i++) {}
|
||||
|
||||
// Case 8: Two variables are used in a loop condition, but one of them
|
||||
// is being modified prior to being used, and the other one is not modified.
|
||||
while (++n < m) {}
|
||||
do {} while (++n < m);
|
||||
for (new i = 0; ++i < m; ) {}
|
||||
|
||||
// Case 9: Two variables are used in a loop condition, but one of them
|
||||
// is static and it's modified inside the loop body, and the other one
|
||||
// is a stack variable and it's left unmodified.
|
||||
while (st < m) { st++; }
|
||||
|
||||
// Case 10: Warnings 250 and 251 may be inaccurate when an array is indexed
|
||||
// inside a loop condition. The problem is that we can't memoize the array
|
||||
// that is being indexed by a variable, to unset the "uLOOPVAR" flag for the
|
||||
// array symbol later when the variable is modified.
|
||||
// This is why I had to completely disable those diagnostics for arrays.
|
||||
{
|
||||
new a[3] = { 0, 0, 1 };
|
||||
n = random(sizeof(a));
|
||||
while (a[n] == 0) // Shouldn't warn about "n" not being modified
|
||||
a[n] = random(3);
|
||||
for (new i = 0; a[i++] == 0; ) {} // shouldn't warn about "a" not being modified
|
||||
for (a[0] = 0, m = 10; a[0] < m; ++a[0]) {} // shouldn't warn about "m" not being modified
|
||||
}
|
||||
|
||||
// Case 11: Just as with arrays, warnings 250 and 251 are disabled when
|
||||
// there's a function call inside a loop condition, as those diagnostics
|
||||
// may be inaccurate otherwise.
|
||||
new File:f = fopen("test.txt", io_read);
|
||||
new line[128];
|
||||
while (fread(f,line,sizeof(line),false) < m) {} // shouldn't warn about "f" or "m" not being modified
|
||||
do {} while (fread(f,line,sizeof(line),false) < m); // shouldn't warn about "f" or "m" not being modified
|
||||
fclose(f);
|
||||
|
||||
// Case 12: Warnings 250 and 251 shouldn't trigger when at least one global
|
||||
// variable is used inside the loop condition, as globals can be modified
|
||||
// from a function called from the loop body and currently there's no easy
|
||||
// way to track this.
|
||||
while (n < glbvar) {}
|
||||
do {} while (n < glbvar);
|
||||
for (new i = 0; i < glbvar; ) {}
|
||||
|
||||
// Case 13: Warnings 250 and 251 shouldn't trigger when the loop counter
|
||||
// variable is passed to a function by reference.
|
||||
while (n < 10) UseVarByRef(n);
|
||||
while (n < m) UseVarByRef(n);
|
||||
|
||||
// Case 14: While const references for single function arguments are
|
||||
// meaningless and there's warning 238 for this, such references still
|
||||
// shouldn't affect warnings 250 and 251, as variables passed by const
|
||||
// references aren't counted as modified.
|
||||
while (n < 10) UseVarByConstRef(n); // warning 250: variable "n" used in loop condition not modified in loop body
|
||||
while (n < m) UseVarByConstRef(n); // warning 251: none of the variables used in loop condition are modified in loop body
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user