/* $Id: comcheck.c,v 1.8 2001/08/26 16:24:12 moniot Rel $ Routines to check common block agreement */ /* Copyright (c) 2001 by Robert K. Moniot. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Acknowledgement: the above permission notice is what is known as the "MIT License." */ /* Shared functions defined: check_com_usage() Checks usage status of common blocks & vars */ #include #include #include "ftnchek.h" #include "symtab.h" #include "pgsymtab.h" /* Local routines defined. */ PROTO(PRIVATE void check_nameclash,(void)); PROTO(PRIVATE void com_block_usage,( char *name, ComListHeader *cl1 )); PROTO(PRIVATE void com_cmp_lax,( char *name, ComListHeader *c1, ComListHeader *c2 )); PROTO(PRIVATE void com_cmp_strict,( char *name, ComListHeader *c1, ComListHeader *c2 )); PROTO(PRIVATE void com_element_usage,( char *name, ComListHeader *r_cl, ComListElement *r_list, int r_num )); PROTO(PRIVATE void print_marked_com_elts,(ComListElement *r_list, int r_num)); #ifdef DEBUG_COM_USAGE PROTO(PRIVATE void print_comvar_usage,( ComListHeader *comlist )); #endif #define pluralize(n) ((n)==1? "":"s") /* singular/plural suffix for n */ void check_comlists(VOID) /* Scans global symbol table for common blocks */ { int i; int model_n; ComListHeader *first_list, *model, *clist; /* Check for name clashes with subprograms */ if(f77_common_subprog_name) { check_nameclash(); } if(COMCHECK_OFF) return; for (i=0; inumargs; clist = model; while( (clist=clist->next) != NULL ){ if(clist->numargs >= model_n /* if tie, use earlier */ /* also if model is from an unvisited library module, take another */ || irrelevant(model) ) { model = clist; model_n = clist->numargs; } } if( irrelevant(model) ) continue; /* skip if irrelevant */ /* Check consistent SAVEing of block: If SAVEd in one module, must be SAVEd in all. Main prog is an exception: SAVE ignored there. */ { ComListHeader *saved_list, *unsaved_list; saved_list = unsaved_list = (ComListHeader *)NULL; clist = first_list; while( clist != NULL ){ if(!irrelevant(clist) && clist->module->type != type_byte(class_SUBPROGRAM,type_PROGRAM) ) { if(clist->saved) saved_list = clist; else unsaved_list = clist; } clist = clist->next; } if(saved_list != (ComListHeader *)NULL && unsaved_list != (ComListHeader *)NULL) { cmp_error_count = 0; (void)comcmp_error_head(glob_symtab[i].name, saved_list, "not SAVED consistently"); com_error_report(saved_list,"is SAVED"); com_error_report(unsaved_list,"is not SAVED"); } } /* Now check agreement of common lists */ clist = first_list; while( clist != NULL ){ if(clist != model && !irrelevant(clist)) { if(comcheck_by_name) com_cmp_strict(glob_symtab[i].name,model,clist); else com_cmp_lax(glob_symtab[i].name,model,clist); } clist = clist->next; } } } } /* check_comlists */ /* Common-list check for comcheck_type or comcheck_length (formerly strictness levels 1 & 2) */ PRIVATE void #if HAVE_STDC com_cmp_lax(char *name, ComListHeader *c1, ComListHeader *c2) #else /* K&R style */ com_cmp_lax(name,c1,c2) char *name; ComListHeader *c1,*c2; #endif /* HAVE_STDC */ { int i1,i2, /* count of common variables in each block */ done1,done2, /* true when end of block reached */ type1,type2; /* type of variable presently in scan */ unsigned long len1,len2, /* length of variable remaining */ size1,size2, /* unit size of variable */ word1,word2, /* number of "words" scanned */ words1,words2, /* number of "words" in block */ defsize1,defsize2, /* default size used? */ jump; /* number of words to skip next in scan */ int byte_oriented=FALSE, /* character vs numeric block */ type_clash; /* flag for catching clashes */ int n1=c1->numargs,n2=c2->numargs; /* variable count for each block */ ComListElement *a1=c1->com_list_array, *a2=c2->com_list_array; /* Count words in each list */ words1=words2=0; for(i1=0; i1numargs, n2 = c2->numargs; ComListElement *a1 = c1->com_list_array, *a2 = c2->com_list_array; if(comcheck_length) { n = (n1 > n2) ? n2: n1; if(n1 != n2){ char msg[15+3*sizeof(n1)]; cmp_error_count = 0; (void)comcmp_error_head(name,c1,"varying length:"); (void)sprintf(msg,"Has %d variable%s", n1,pluralize(n1)); com_error_report(c1,msg); (void)sprintf(msg,"Has %d variable%s", n2,pluralize(n2)); com_error_report(c2,msg); } } #ifdef DEBUG_PGSYMTAB if(debug_latest){ (void)fprintf(list_fd,"block %s",name); (void)fprintf(list_fd,"\n\t1=in module %s line %u file %s (%s)", c1->module->name, c1->line_num, c1->topfile c1->filename); (void)fprintf(list_fd,"\n\t2=in module %s line %u file %s (%s)", c2->module->name, c2->line_num, c2->topfile, c2->filename); } #endif if(comcheck_type) { cmp_error_count = 0; for (i=0; iinfo.comlist; cmp_error_count = 0; (void)comcmp_error_head(hashtab[i].name,clh, "has same name as a subprogram (nonstandard)"); com_error_report(clh,"Declared as common block"); for(alist=hashtab[i].glob_symtab->info.arglist;alist!=NULL; alist=alist->next) { if(alist->is_defn) { break; } } /* if not declared: use first reference */ if(alist==NULL) { sub_error_report( hashtab[i].glob_symtab->info.arglist, "Referenced as subprogram"); } else { sub_error_report(alist, "Declared as subprogram"); } } } } #ifdef DEBUG_COM_USAGE PRIVATE void print_comvar_usage(comlist) ComListHeader *comlist; { int i, count; ComListElement *c; count = comlist->numargs; c = comlist->com_list_array; /* prints out caller module and any_used, any_set flags in CLhead */ (void)fprintf(list_fd, "\nModule %s any_used %u any_set %u\n", comlist->module->name, comlist->any_used, comlist->any_set); if((comlist->any_used || comlist-> any_set||1) ){ for (i=0; inumargs; ref_cl= cl1; cur_cl = cl1; while (cur_cl!=NULL){ /* traverses CLheads */ if(! irrelevant(cur_cl) ) { if (cur_cl->any_used){ /* stores TRUE if any are TRUE */ block_any_used = TRUE; } if (cur_cl->any_set){ /* stores TRUE if any are TRUE */ block_any_set = TRUE; } if( ! (cur_cl->any_used || cur_cl->any_set) && ! cur_cl->module->defined_in_include ) { block_unused_somewhere = TRUE; } /* if any_set and any_used false after this loop block never used */ if (cur_cl->numargs > ref_n){ /* find largest array */ ref_cl = cur_cl; ref_n = cur_cl->numargs; } /* end of if */ }/* end if not irrelevant */ cur_cl = cur_cl->next; } if(irrelevant(ref_cl)) /* Block not declared by modules in calltree */ return; if(! (block_any_used || block_any_set) ) { /* Totally unused */ if(usage_com_block_unused) { cmp_error_count = 0; (void)comcmp_error_head(name,ref_cl,"unused anywhere"); } } else { /* If block used somewhere but not everywhere, report it. */ if(block_unused_somewhere && usage_com_block_unused) { cmp_error_count = 0; (void)comcmp_error_head(name,ref_cl, "unused in the following modules:"); cur_cl = cl1; while (cur_cl!=NULL){ /* traverses CLheads */ if(! irrelevant(cur_cl) ) { if( ! (cur_cl->any_used || cur_cl->any_set) && ! cur_cl->module->defined_in_include ) { com_error_report(cur_cl,"Unused"); } } cur_cl = cur_cl->next; } }/* end if block_unused_somewhere */ if(! comcheck_by_name) { /* If not variablewise checking, just give general warnings. */ if (!block_any_set){ if(usage_com_var_uninitialized) { cmp_error_count = 0; (void)comcmp_error_head(name,ref_cl, "No elements are set, but some are used."); } } if (!block_any_used){ if(usage_com_var_set_unused) { cmp_error_count = 0; (void)comcmp_error_head(name,ref_cl, "No elements are used, but some are set."); } } } else { /* strictness == 3 */ /* Now go thru the details for each element */ /* First, malloc up a temporary list and copy ref_cl and its list there so the original is not clobbered (used later in arg usage checks for common aliasing) */ ComListHeader *new_ref_cl; ComListElement *new_ref_list; if( (new_ref_cl=(ComListHeader *)calloc(1,sizeof(ComListHeader))) == (ComListHeader *)NULL || (new_ref_list=(ComListElement *)calloc(ref_cl->numargs,sizeof(ComListElement))) == (ComListElement *)NULL ) { oops_message(OOPS_FATAL,NO_LINE_NUM,NO_COL_NUM, "Cannot alloc space for common block ref list"); } *new_ref_cl = *ref_cl; /* Copy the header over to temporary */ ref_list = ref_cl->com_list_array; for(j=0; jnumargs; j++) { /* Copy the array as well */ new_ref_list[j] = ref_list[j]; } ref_cl = new_ref_cl; /* Now make the temporary the one we use */ ref_list = new_ref_list; ref_cl->any_used = block_any_used; ref_cl->any_set = block_any_set; /* traversing elements in arrays and storing OR'd values in largest array*/ cur_cl = cl1; while (cur_cl!=NULL){ if(! irrelevant(cur_cl) ) { c = cur_cl->com_list_array; n = cur_cl->numargs; for (j=0; jnext; } com_element_usage(name, ref_cl, ref_list, ref_n); /* Free up the temporary ref list */ free(new_ref_cl); free(new_ref_list); } } } /* Routine to print a list of common-block elements whose marked flag has been set. */ PRIVATE void #if HAVE_STDC print_marked_com_elts(ComListElement *r_list, int r_num) #else /* K&R style */ print_marked_com_elts(r_list, r_num) ComListElement *r_list; /* list of elements, some marked */ int r_num; /* number of elements in whole list */ #endif /* HAVE_STDC */ { int i; COLNO_t col; for (i=0,col=78; i 78 ) { (void)fprintf(list_fd,"\n "); col = 4+(int)strlen(r_list[i].name); } (void)fprintf(list_fd, " %s", r_list[i].name); } } } PRIVATE void #if HAVE_STDC com_element_usage(char *name, ComListHeader *r_cl, ComListElement *r_list, int r_num) #else /* K&R style */ com_element_usage(name, r_cl, r_list, r_num) char *name; ComListHeader *r_cl; ComListElement *r_list; int r_num; #endif /* HAVE_STDC */ { int i, warnings; if (r_cl->any_used || r_cl->any_set){ /* if false block not used */ if(usage_com_var_uninitialized) { warnings = 0; for (i=0; i 0) { cmp_error_count = 0; (void)comcmp_error_head(name,r_cl, "Elements used but never set:"); if(warnings == r_num) { (void)fprintf(list_fd," all"); } else { print_marked_com_elts(r_list, r_num); } } } if(usage_com_var_set_unused) { warnings = 0; for (i=0; i 0) { cmp_error_count = 0; (void)comcmp_error_head(name,r_cl, "Elements set but never used:"); if(warnings == r_num) { (void)fprintf(list_fd," all"); } else { print_marked_com_elts(r_list, r_num); } } } if(usage_com_var_unused) { warnings = 0; for (i=0; i 0) { cmp_error_count = 0; (void)comcmp_error_head(name,r_cl, "Elements never used, never set:"); if(warnings == r_num) { /* can't happen but keeps code alike */ (void)fprintf(list_fd," all"); } else { print_marked_com_elts(r_list, r_num); } } } } else{ /* This cannot be reached if called only when block is used */ if(usage_com_block_unused) { cmp_error_count = 0; (void)comcmp_error_head(name,r_cl, "not used."); } } /* any_used and any_set are both false */ }