cca.cxx

Go to the documentation of this file.
00001 // cyclomatic complexity analyzer
00002 
00003 //
00004 // Copyright 2002, 2010, 2011 Lowell Boggs Jr.
00005 //
00006 // This file or directory, containing source code for a computer program,
00007 // is Copyrighted by Lowell Boggs, Jr.  987 Regency Drive, Lewisville
00008 // TX (USA), 75067.  You may use, copy, modify, and distribute this
00009 // source file without charge or obligation so long as you agree to
00010 // the following:
00011 //
00012 //  1.  You must indemnify Lowell Boggs against any and all financial
00013 //      obligations caused by its use, misuse, function, or malfunction.
00014 //      Further, you acknowledge that there is no warranty of any kind,
00015 //      whatsoever.
00016 //
00017 //  2.  You agree not to attempt to patent any portion of this original
00018 //      work -- though you may attempt to patent your own extensions to
00019 //      it if you so choose.
00020 //
00021 //  3.  You keep this copyright notice with the file and all copies
00022 //      of the file and do not change it anyway except language translation.
00023 //
00024 // You are responsible for enforcing your own compliance with these
00025 // conditions and may not use this source file if you cannot agree to the
00026 // above terms and conditions.
00027 // 
00028 
00093 
00094 
00095 
00096 
00097 
00098 
00099 #include <cxxtls/cpp_token_stream.h>
00100 #include <cxxtls/file.h>
00101 #include <cxxtls/fmtd.h>
00102 
00103 #include <portable_math.h>
00104 #include <portable_strstream.h>
00105 #include <portable_io.h>
00106 #include <cxxtls/options.h>
00107 
00108 #include <cstdlib>
00109 #include <fstream>
00110 #include <iostream>
00111 #include <algorithm>
00112 #include <list>
00113 #include <memory.h>
00114 #include <iomanip>
00115 #include <set>
00116 
00117 using namespace std;
00118 using namespace cxxtls;
00119 
00120 static void parse_file(FileName const &);
00121 
00122 static ostreambuf_iterator<char>* output;
00123 
00124 static bool read_filenames_from_stdin = false; 
00125 static bool log_all_tokens_to_stderr  = false; 
00126 static bool log_filenames_to_stderr   = false; 
00127 static bool grep_style_output         = true;  
00128 static bool collapse_elseif           = true;  
00129 static bool collapse_return           = true;  
00130 static bool print_summary             = false; 
00131 static bool print_functions           = true;  
00132 static bool force_java_file           = false; 
00133 
00134 static bool log_definitions           = false; 
00135 
00136 static string ifKeyWord               ("if");
00137 static string elseKeyWord             ("else");
00138 static string whileKeyWord            ("while");
00139 static string doKeyWord               ("do");
00140 static string forKeyWord              ("for");
00141 static string switchKeyWord           ("switch");
00142 static string caseKeyWord             ("case");
00143 static string defaultKeyWord          ("default");
00144 static string operatorKeyWord         ("operator");
00145 static string templateKeyWord         ("template");
00146 static string classKeyWord            ("class");
00147 static string structKeyWord           ("struct");
00148 static string enumKeyWord             ("enum");
00149 static string typedefKeyWord          ("typedef");
00150 static string namespaceKeyWord        ("namespace");
00151 static string usingKeyWord            ("using");
00152 static string friendKeyWord           ("friend");
00153 static string externKeyWord           ("extern");
00154 static string publicKeyWord           ("public");
00155 static string privateKeyWord          ("private");
00156 static string protectedKeyWord        ("protected");
00157 static string unionKeyWord            ("union");
00158 static string typenameKeyWord         ("typename");
00159 static string constKeyWord            ("const");
00160 static string staticKeyWord           ("static");
00161 static string catchKeyWord            ("catch");
00162 static string tryKeyWord              ("try");
00163 static string returnKeyWord           ("return");
00164 static string throwKeyWord            ("throw");
00165 static string extendsKeyWord          ("extends");  // Java only!
00166 static string implementsKeyWord       ("implements"); // Java only!
00167 
00168 static void printInfo(); // print a discussion of the algorithm.
00169 static void printSummary();
00170 
00171 static set<string> returnSynonyms;
00172 
00173 static bool isJavaFile = false; // reset in parse_file on every file.
00174 
00175 int main(int argc, char **argv, char **environ)
00176 {
00177 
00178 # ifdef _MSC_VER
00179   
00180     // force the output to be a binary stream and we'll control eht end of line sequencing our
00181     // selves
00182   
00183     cout.flush();
00184   
00185     setmode(1, O_BINARY);
00186 
00187 # endif
00188 
00189   output = new ostreambuf_iterator<char>(cout);
00190 
00191   //
00192   // parse the options
00193   //
00194 
00195   ProgramOptions ops("-SO,-S,-R;-m,-info,-r,-e,-v,-files,-tokens,-stdin,-resume,-help,-h,--help,-j",
00196                      argc,
00197                      argv,
00198                      environ
00199                     );
00200 
00201   if(ops.option("-v"))
00202   {
00203     cout << "cca version 1.17" << endl;
00204 
00205       // revision history:
00206       //
00207       //  1.17 a.  Improved #if intereptation in order to eliminate
00208       //           the erroneous parsing of both the if and the else
00209       //           clauses.  Note that no real macro processing is going on
00210       //           in this change -- we are just making sure that we don't
00211       //           process both the if and the else.  Since header files
00212       //           often being with
00213       //
00214       //              #ifndef macroname
00215       //              #define macroname
00216       //                ...
00217       //              #endif
00218       //
00219       //           It is necessary for #ifndef to actually be taken.  However,
00220       //           most other #if tests will NOT be taken.  That is, generally
00221       //           we assume that the #if test fails except for the following:
00222       //
00223       //             #if 1  // is taken
00224       //             #if true // is taken
00225       //             #if TRUE // is taken
00226       //
00227       //           Almost all other tests are not.
00228       //
00229       //           This is just a way to eliminate duplicate code logic that really
00230       //           messes up the parser -- but it might mean your real code doesn't
00231       //           get parsed -- depending on how your #ifs work...
00232       //
00233       //           If you want REAL accuracy in macro expansion, create .E files
00234       //           and run cca.exe on them instead of the raw source.  For example:
00235       //           in your makefile add the following:
00236       //
00237       //               .c.E:
00238       //                   g++ -E $(CC_INCLUDES) $*.c >$@
00239       //            
00240       //
00241       //  1.16 a.  added support for class template specializations.
00242       //
00243       //  1.15 a.  Updated support for badly formed programs based on a common pattern
00244       //           in the boost library:  the use of a macro to generate a class name:
00245       //              struct MACRO(x) { ... }
00246       //
00247       //  1.14 a.  Added -j and fixed various bugs in c++ processing.
00248       //
00249       //  1.13 a.  Removed the need for the -dcl option
00250       //
00251       //  1.12 a.  Fixed major bug:  Now it is possible to correctly detect the
00252       //           following class name:
00253       //
00254       //              class CursorWindow::Dialog::Impl { ... }
00255       //
00256       //  1.11 a.  Add -dcl option to support vc++ goofiness
00257       //
00258       //  1.10 a.  Improved option mismatch detection.  -R doesn't go with -m or -r
00259       //
00260       //  1.9  a.  Fixed a bug in the handling of try/catch blocks
00261       //
00262       //  1.8  a.  added the "-SO" and "-S" option to trigger the printing of a summaries.
00263       //
00264       //  1.7  a.  added "-R returnSynonum" to allow you to define one or
00265       //           more synonyms for the return statement.  Typically this
00266       //           would be used to add "exit" as a synonym for return
00267       //           when -m and -r are not used.  Multiple -R's are allowed.
00268       //
00269       //  1.6  a.  fixed a bug in eat_template_parms to prevent it from walking
00270       //           across ;, {, or }.
00271       //
00272       //  1.5  a.  basic support for java, no thorough testing though.
00273       //           class static constructor method gets ignored for sure.
00274       //
00275       //  1.4  a.  array initializer value lists are no longer mistaken to
00276       //           function bodies.
00277       //
00278       //  1.3  a.  added -m option to more closely approximate the McCabe
00279       //           metric for if-else clasues.
00280       //       b.  added the -info option to print a discussion of the
00281       //           algorithm.
00282       //
00283       //  1.2  a.  add -e option to expand else-if complexity as it if were
00284       //           two statements instead of one (increasing CC number).
00285       // 
00286       //  1.1  a.  removed -defs option (its the only choice now).
00287       //       b.  added catch(){} block processing.  You can only get
00288       //           one executed at a time, so they define alternatives
00289       //           no sequences of code.  The largest pathcount will be
00290       //           the final pathcount after the blocks.
00291       //       c.  removed -nostats option (it made no sense in this program).
00292       //       d.  removed -grep option (default)
00293       //       e.  removed -resume option (it made no sense here)
00294       //
00295       //  1.0  everything basically works and else-if and switch
00296       //       cases are treated as single big structures, of which only
00297       //       one block will executed, rather than being separate structures
00298       //       in which any block could execute.  You get smaller path
00299       //       counts in 1.0 than you got in 0.9 for else-if and switch
00300       //       blocks.
00301 
00302     exit(0);
00303   }
00304 
00305 
00306   log_filenames_to_stderr   =  ops.option("-files");
00307   read_filenames_from_stdin =  ops.option("-stdin");
00308   log_all_tokens_to_stderr  =  ops.option("-tokens");
00309   collapse_elseif           = !ops.option("-e"); 
00310   collapse_return           = !ops.option("-r");
00311   print_summary             =  ops.option("-S") || ops.option("-SO");
00312   print_functions           = !ops.option("-SO");
00313   force_java_file           =  ops.option("-j");
00314 
00315 
00316   if(ops.option("-m"))
00317   {
00318      collapse_elseif = false;
00319      collapse_return = false;
00320   }
00321 
00322   if(ops.option("-info"))
00323   {
00324      printInfo();
00325      exit(0);
00326   }
00327 
00328   if( ops.option("-R") )
00329   {
00330       // there is at least one synonym for the return statement
00331 
00332       ProgramOptions::Option const &r = ops.option("-R");
00333 
00334       ProgramOptions::value_iterator first = r.begin(),
00335                                      last  = r.end();
00336 
00337       while(first != last)
00338       {
00339           string const &current = *first++;
00340 
00341           returnSynonyms.insert(current);
00342       }
00343 
00344   }
00345 
00346   if(    ( ops.option("-r") || ops.option("-m") || ops.option("-e") )
00347      &&  returnSynonyms.size() != 0
00348     )
00349   {
00350       cerr << "cxa.exe: ERROR: -R not compatible with -e, -r, or -m" << endl;
00351       exit(1);
00352   }
00353 
00354 
00355   if(ops.option("-help")  ||
00356      ops.option("--help") ||
00357      ops.option("-h")
00358     )
00359   {
00360     FileName program(ops.argv[0]);
00361 
00362     cerr << "Usage:" << endl
00363          << "  "     << program.basename()
00364          << "  [options] [.c and .h file names]" << endl
00365          << "Options:" << endl
00366          << " Basic Options" << endl
00367          << "  -info    print a description of the algorithm" << endl
00368          << "  -help    print this help message" << endl
00369          << "  -h       ditto" << endl
00370          << "  --help   ditto" << endl
00371          << "  -stdin   read the .c and .h file names from stdin" << endl
00372          << "           note:  one filename per line" << endl
00373          << "  -tokens  log all tokens to stderr" << endl
00374          << "  -files   log processed files to stderr" << endl
00375          << "  -S       add a summary to the output" << endl
00376          << "  -SO      produce ONLY summary output" << endl
00377          << " Logic modification settings" << endl
00378          << "   -m      McCabe mode.  Sets -e, -r, etc." << endl
00379          << "   -e      suppresses else-if collapsing" << endl
00380          << "   -r      suppreses return-statment collapsing in if-else clauses" << endl
00381          << "             * does not apply to else-if clauses!" << endl
00382          << "   -R syn  Define a synonym for 'return'" << endl
00383          << "             * syn usually names a function, like exit." << endl
00384          << "             * multiple -R syns are allowed" << endl
00385          << "             * not compatible with -e, -r, or -m" << endl
00386          << "Output Format:" << endl
00387          << " Example" << endl
00388          << "   filename.c:123:  ::main  S=20 P=8 C=3" << endl
00389          << " Explanation:" << endl
00390          << "   function ::main was defined in file \"filename.c\" at " << endl
00391          << "   line 123.  It has 20 statements with 8 paths through the" << endl
00392          << "   code, and has a cyclomatic complexity number of 3." << endl
00393          ;
00394 
00395     exit(0);
00396   }
00397 
00398   ops.argv.erase(ops.argv.begin());  // remove the program name from the
00399                                      // non-optional program arguments
00400 
00401 
00402   // get the list of file names to work on
00403 
00404   list<FileName> files;
00405 
00406   if(read_filenames_from_stdin)
00407   {
00408     string tmp;
00409 
00410     while(!cin.eof())
00411     {
00412       tmp.resize(0);
00413 
00414       getline(cin, tmp);
00415 
00416       if(tmp.size() == 0)
00417         break;
00418         
00419         
00420       files.push_back(tmp);
00421 
00422     }
00423   }
00424   else
00425   {
00426     size_t i;
00427 
00428     for(i=0; i < ops.argv.size(); ++i)
00429     {
00430       files.push_back(ops.argv[i]);
00431     }
00432 
00433   }
00434 
00435   // parse the named files
00436 
00437   list<FileName>::iterator l = files.begin();
00438   list<FileName>::iterator g = files.end();
00439 
00440 
00441   for(; l != g; ++l )
00442   {
00443     if(!l->exists())
00444     {
00445       cerr << *l << " error, file does not exist" << endl;
00446     }
00447     else
00448     if(!l->is_dir())
00449       parse_file(*l);
00450   }
00451 
00452   if(print_summary)
00453       printSummary();
00454 
00455   exit(0);
00456 }
00457 
00458 static void parse_declarations(CPP_Buffer_Token_Source &s);
00459 static void log_symbol(string type, string name, string file, int line, bool include_scope=true);
00460 
00461 class DefineHandler
00462 : public CPP_Token_Stream_Prep
00463   //
00467   //
00468 {
00469 public:
00470 
00471   static vector<bool> ifStack_;
00472 
00473   static bool tokensVisible_;
00474 
00475   static void computeVisibility()
00476   {
00477       // even 1 false on the stack means tokens are not currently visible.
00478 
00479       tokensVisible_ = true;
00480 
00481       for(size_t i = 0; i < ifStack_.size(); ++i)
00482          if(!ifStack_[i])
00483          {
00484             tokensVisible_ = false;
00485          }
00486 
00487   }
00488 
00489   void ifHandler(string const &define, string const &file, int line) const
00490   {
00491       // assume define is not empty on entry
00492 
00493       static string ifKeyword("if");
00494       static string ifdefKeyword("ifdef");
00495       static string ifndefKeyword("ifndef");
00496       static string definedKeyword("defined");
00497       static string elseKeyword("else");
00498       static string endifKeyword("endif");
00499 
00500       char c = define[0];
00501 
00502       string name=define.substr(0,  define.find_first_of(" \t\n\r") );
00503 
00504 
00505       if(c == 'i')
00506       {
00507           if(name == ifKeyword)
00508           {
00509              bool computedVisibility = false;
00510 
00511              bool searchIt=true;
00512 
00513              for(size_t index = name.size(); index < define.size()-1; ++index)
00514              {
00515                 char f = define[index];
00516                 char n = define[index+1];
00517 
00518                 static string trueKeyword("true");
00519                 static string TRUEKeyword("TRUE");
00520 
00521 
00522                 if(   (   ( f == '1' && isspace(n) )
00523                        || ( f == 't' && define.substr(index, 4) == "true")
00524                        || ( f == 'T' && define.substr(index, 4) == "TRUE")
00525                       )
00526                   )
00527                 {
00528                    computedVisibility = true;
00529                    searchIt= false;
00530                    break;
00531                 }
00532 
00533              }
00534 
00535              if(searchIt)
00536              {
00537                  string::const_iterator loc = search(define.begin(), 
00538                                                      define.end(), 
00539                                                      definedKeyword.begin(), 
00540                                                      definedKeyword.end()
00541                                                     );
00542                  
00543                  if(loc != define.end())
00544                  {
00545                      // defined is false, so we need to see if operation has been notted.
00546                  
00547                      while(loc != define.begin())
00548                      {
00549                         if(*loc == '!')
00550                         {
00551                            computedVisibility = true;
00552                            break;
00553                         }
00554                  
00555                         --loc;
00556                  
00557                      }
00558                  
00559                  }
00560              }
00561 
00562              ifStack_.push_back(computedVisibility);
00563           }
00564           else
00565           if(name == ifdefKeyword)
00566           {
00567              ifStack_.push_back(false);
00568           }
00569           else
00570           if(name == ifndefKeyword)
00571           {
00572              ifStack_.push_back(true);
00573           }
00574 
00575           computeVisibility();
00576 
00577       }
00578       else
00579       if(c == 'e')
00580       {
00581           if(name == elseKeyword)
00582           {
00583               if(!ifStack_.empty())
00584               {
00585                  ifStack_.back() = !ifStack_.back();
00586               }
00587 
00588           }
00589           else
00590           if(name == endifKeyword)
00591           {
00592               if(!ifStack_.empty())
00593                 ifStack_.pop_back();
00594 
00595           }
00596 
00597           computeVisibility();
00598       }
00599 
00600 
00601   }
00602 
00603   void operator() (string const &define, string const &file, int line) const
00604   {
00605     string::const_iterator f(define.begin());
00606     string::const_iterator g(define.end());
00607 
00608     // only handle #defines
00609 
00610     if( f == g)
00611       return;
00612 
00613     if( *f != 'd')
00614     {
00615       return ifHandler(define, file, line);
00616 
00617       return;
00618     }
00619     // skip the word 'define'
00620 
00621     while(f != g && *f != ' ') ++f;
00622 
00623     // skip blanks after define
00624 
00625     while(f != g && (*f == ' ' || *f == '\n') ) ++f;
00626 
00627     if(f == g)
00628       return;  // missing define variable name
00629 
00630     // skip past the text in the define variable name
00631 
00632     string::const_iterator l(f);
00633 
00634     while(l != g &&
00635           (
00636             (*l >= 'a' && *l <= 'z') ||
00637             (*l >= 'A' && *l <= 'Z') ||
00638             (*l >= '0' && *l <= '9') ||
00639             (*l == '_')
00640           )
00641          ) ++l;
00642 
00643     // f,l now defines the name of the define variable
00644 
00645     static string define_name("define");
00646 
00647     log_symbol(define_name, string(f,l), file, line, false);
00648 
00649   }
00650 };
00651 
00652 vector<bool> DefineHandler::ifStack_;
00653 bool DefineHandler::tokensVisible_ = true;
00654 
00655 static void parse_file(FileName const& name)
00656 //
00657 // Parse a file and print out the definitions contained
00658 // therein.
00659 //
00660 {
00661   if(force_java_file)
00662   {
00663      isJavaFile = true;
00664   }
00665   else
00666   {
00667      string ext = name.extension();
00668    
00669    
00670      if(ext == ".java" || ext == ".JAVA" )
00671      {
00672         isJavaFile = true; 
00673      }
00674      else
00675        isJavaFile = false;
00676   }
00677 
00678   FileContents buffer;
00679 
00680   name.slurp(&buffer);
00681 
00682   if(!buffer.ok())
00683   {
00684     cerr << "Error opening " << name << ", " << buffer.error() << endl << flush;
00685     exit(1);
00686   }
00687 
00688   DefineHandler dh;
00689 
00690   if(log_filenames_to_stderr)
00691     cerr << name << endl;
00692 
00693   CPP_Buffer_Token_Source s(buffer.begin(), buffer.end(), name, &dh);
00694 
00695   parse_declarations(s);
00696 
00697 }
00698 
00699 static CPP_Token                token;
00700 static CPP_Buffer_Token_Source *stream;
00701 
00702 class FunctionScope
00704 {
00705 public:
00706 
00707   typedef unsigned long long paths_t;   // 64 bit unsigned value holding the count of the
00708                                         // total number of paths through the code.
00709 
00710 private:
00711 
00712    // keep these in sycn with the operator= method
00713 
00714    string scopeName;  
00715    string scopeMember;  // function name within the scope
00716    string scopeFile_;    // file where func location
00717    int    scopeLine_;    // line in scopeFile_ where func defined
00718 
00719    int    statements_;  // only applies to functions
00720 
00721    paths_t  paths_;       // only applies to functions
00722 
00723 public:
00724 
00725    FunctionScope &operator=( FunctionScope const &rhs )
00726    {
00727      scopeName   = rhs.scopeName;
00728      scopeMember = rhs.scopeMember;
00729      scopeFile_   = rhs.scopeFile_;
00730      scopeLine_   = rhs.scopeLine_;
00731 
00732      statements_ = rhs.statements_;
00733      paths_      = rhs.paths_;
00734 
00735 
00736      return *this;
00737    }
00738 
00739    FunctionScope(FunctionScope const &rhs)
00740    {
00741       *this = rhs;
00742    }
00743 
00744 
00745    void resetCounters() 
00746       // go back to the state just before the { brace that starts
00747       // a function
00748    { 
00749       statements_ = 0;
00750       paths_      = 1;
00751       
00752    }
00753 
00754    FunctionScope()
00755    : scopeName()
00756    {
00757       scopeLine_   = 0;
00758       scopeMember.resize(0);
00759       scopeFile_.resize(0);
00760 
00761       resetCounters();
00762    }
00763 
00764    operator string const &() const { return scopeName; }
00765 
00766    string scopeMemberName() const { return scopeName + scopeMember; }
00767 
00768    string const &scopeMemberValue() const { return scopeMember; } 
00769 
00770    inline friend ostream &operator<<( ostream &os, FunctionScope const &me)
00771    {
00772       int CyclomaticComplexityNumber = int( 0.4999 + log2( me.paths_) );
00773 
00774       if(CyclomaticComplexityNumber < 1)
00775          CyclomaticComplexityNumber = 1;
00776 
00777       return os << me.scopeFile_ << ':' << me.scopeLine_      << ": "  
00778                 << me.scopeName << me.scopeMember             << ' '
00779                 << "S="        << me.statements_              << ' '  
00780                 << "P="        << me.paths_                   << ' '
00781                 << "C="        << CyclomaticComplexityNumber
00782                 ;
00783    }
00784 
00785    void setScope(string const &s) { scopeName = s; }
00786 
00787    void setScopeMember(string const &m) { scopeMember = m; }
00788    
00789    void setScopeFileInfo(string const &file, int line) { scopeFile_ = file; scopeLine_ = line; }
00790 
00791    string const &scopeFile() const { return scopeFile_; }
00792    int           scopeLine() const { return scopeLine_; }
00793 
00794    string::const_iterator begin() const { return scopeName.begin(); }
00795    string::const_iterator end()   const { return scopeName.end(); }
00796 
00797    int      statements()    const { return statements_; }
00798    paths_t  paths()         const { return paths_;   }
00799    
00800    void addStatement()    { ++statements_; }
00801 
00802    void doublePaths()     
00803    {
00804        paths_t tmp(paths_);
00805    
00806        paths_ <<= 1;   
00807 
00808        if(paths_ < tmp)
00809        {
00810          // we should have been increasing the paths_ variable, not decreasing it
00811          // so we must have rounded off
00812 
00813          paths_ = paths_t(-1);  // truncate to the largest possible number
00814 
00815        }
00816    }
00817 
00818    void multiplyPaths( unsigned count)
00819    {
00820        paths_t tmp(paths_);
00821    
00822        paths_ *= count;
00823 
00824        if(paths_ < tmp)
00825        {
00826          // we should have been increasing the paths_ variable, not decreasing it
00827          // so we must have rounded off
00828 
00829          paths_ = paths_t(-1);  // truncate to the largest possible number
00830 
00831        }
00832    }
00833 
00834    void setPaths(paths_t count)   { paths_ = count; }
00835 
00836 };
00837 
00838 FunctionScope outer_scope;
00839 
00840 struct SummaryData
00841 {
00842     unsigned long int statements_;
00843     double            paths_;
00844     unsigned long int functions_;
00845 
00846     SummaryData()
00847     :  statements_(0)
00848     ,  paths_(0)
00849     ,  functions_(0)
00850     {
00851     }
00852 };
00853 
00854 map<string, SummaryData> summary;
00855 
00856 static void printSummary()
00857 {
00858    cout << "\nSUMMARY\n" << endl;
00859 
00860    cout << "INDIVIDUAL SCOPES" << endl;
00861 
00862    cout 
00863        << "\t" << "FUNCS"
00864        << "\t" << "CC#"
00865        << "\t" << "LOC"  
00866        << "\t" << "IN SCOPE" 
00867        << endl;
00868 
00869    map<string, SummaryData>::iterator next = summary.begin(),
00870                                       last = summary.end();
00871 
00872 
00873    SummaryData totals;
00874 
00875    int totalCC = 0;
00876 
00877    while(next != last)
00878    {
00879       string      const &name = next->first;
00880       SummaryData const &info = next->second;
00881 
00882       ++next;
00883 
00884 
00885       int CyclomaticComplexityNumber = int( 0.4999 + log2( info.paths_) );
00886 
00887       if(CyclomaticComplexityNumber < 1)
00888           CyclomaticComplexityNumber = 1;
00889 
00890       cout 
00891            << "\t" << info.functions_
00892            << "\t" << CyclomaticComplexityNumber 
00893            << "\t" << info.statements_ 
00894            << "\t" << name 
00895            << endl;
00896 
00897       totals.functions_  += info.functions_;
00898       totalCC            += CyclomaticComplexityNumber;
00899       totals.statements_ += info.statements_;
00900       totals.paths_      += info.paths_;
00901 
00902    }
00903 
00904    cout << "TOTALS" << endl;
00905 
00906    cout 
00907            << "\t" << totals.functions_
00908            << "\t" << totalCC
00909            << "\t" << totals.statements_ 
00910            << endl;
00911 
00912 
00913 }
00914 
00915 
00916 static void summarize()
00917 {
00918     string scopeName = outer_scope;  // does not include the scope member
00919 
00920     SummaryData &data = summary[scopeName];
00921 
00922     data.paths_      += outer_scope.paths();
00923     data.statements_ += outer_scope.statements();
00924     ++data.functions_;
00925 }
00926 
00927 
00928 
00929 inline static void next_token()
00930 //
00931 // get the next token from the stream and leave it 'token'
00932 //
00933 {
00934   token.type_ = CPP_Token::eof;
00935 
00936   do
00937   {
00938       (*stream)(token);
00939   }
00940   while(!token.type_ == CPP_Token::eof && !DefineHandler::tokensVisible_);
00941 
00942   if(token.type_ == CPP_Token::aln &&
00943      token.text_[0] == 'o' &&
00944      token.text_ == operatorKeyWord
00945     )
00946   {
00947     (*stream)(token);
00948 
00949     if(token.type_ == '(')
00950     {
00951       token.type_ = CPP_Token::aln;
00952 
00953       token.text_ = operatorKeyWord + " " + token.text_;
00954 
00955       CPP_Token tmp;
00956 
00957       (*stream)(tmp);
00958 
00959       token.text_ += tmp.text_;
00960     }
00961     else
00962     {
00963       token.type_ = CPP_Token::aln;
00964 
00965       token.text_ = operatorKeyWord + " " + token.text_;
00966     }
00967 
00968 
00969 
00970   }
00971   else
00972   if(token.type_ == CPP_Token::inv && token.text_.find_first_of(';') < token.text_.size() )
00973   {
00974       // if some screwball conglomeration of tokens accidentally contains a semicolon,
00975       // call that token a semicolon because the tokenizer deals with token groups, not
00976       // just tokens.  So ;;;;;;, is not 5 ;'s but one ;;;;;;; token.  Though it is labeled
00977       // as invalid but that means only that it is invalid in C++.  Normally, C++, does not
00978       // conglomerate unrelated tokens together, but CPP_Token does.
00979       //
00980       // Anyway, multiple semicolons is a common enough error to specifically ignore it --
00981       // especially since ; plays such a key role in this tool.
00982 
00983       token.text_ = ";";
00984       token.type_ = ';';
00985   }
00986 
00987 
00988   if(log_all_tokens_to_stderr)
00989     cerr << token << " \t" << outer_scope.scopeMemberName() << endl;
00990 
00991 }
00992 
00993 inline bool eof()
00994 //
00995 // determine if we are at the end of the token stream
00996 //
00997 {
00998    return token.type_ == CPP_Token::eof;
00999 }
01000 
01001 static bool parse_declaration(bool log_func_forward_decls=false);
01002 
01003 
01004 
01005 static void parse_declarations(CPP_Buffer_Token_Source &s)
01006 //
01007 // parse and print out all definitions in a file
01008 // (don't use this for parsing nested stuff)
01009 {
01010   stream = &s;
01011 
01012   for(next_token(); !eof(); )
01013   {
01014     outer_scope.setScope("::");
01015 
01016     if(parse_declaration())  // automatically calls next_token()
01017       break;
01018   }
01019 
01020   if(!eof())
01021   {
01022     cerr << token << ", error:  expected end of file" << endl;
01023     cerr << __FILE__ << ":" << __LINE__ << endl;
01024   }
01025 
01026 }
01027 
01028 #define ASSUMED_TOKEN(type) \
01029   if(token.type_ != type) \
01030   { \
01031     cerr << token << ", error:  expected " << CPP_Token::type_name(type) << endl; \
01032     cerr << __FILE__ << ":" << __LINE__ << endl; \
01033     return true; \
01034   }
01035 
01036 
01037 
01038 // all parsing functions leave the token that terminated them
01039 // in the input stream.
01040 
01041 static bool parse_class();
01042 static bool parse_namespace();
01043 static bool parse_typedef();
01044 
01045 static bool parse_enum();
01046 static bool parse_extern();
01047 static bool parse_using();
01048 static bool parse_funcvar(bool log_forward_funcs_defs=false,
01049                           bool handling_typedefs=false);
01050 static void eat_template_parms();
01051 static void eat_curly_block();
01052 static void eat_function_body();
01053 
01054 static bool parse_declaration(bool log_func_forward_decls)
01055 {
01056   if(token.type_ == ';' ) // allow randomly placed semicolons
01057   {
01058     next_token();
01059     return false;
01060   }
01061 
01062   if(token.type_ == '{')
01063   {
01064     // allow randomly placed {} blocks
01065 
01066     eat_curly_block();
01067     next_token();
01068     return false;
01069 
01070   }
01071 
01072   if(token.type_ == CPP_Token::eof)
01073     return true;
01074 
01075   if(token.type_ == '=')  // handle oddly placed variable initializations
01076   {
01077      while(token.type_ != ';')
01078      {
01079        if(token.type_ == '{')
01080          eat_curly_block();
01081 
01082        next_token();
01083      }
01084 
01085      return false;
01086   }
01087 
01088 
01089 //  if(token.type_ != CPP_Token::der)
01090 //    ASSUMED_TOKEN(CPP_Token::aln);
01091 
01092   if(token.text_ == templateKeyWord)
01093   {
01094     // eat template<parms>
01095 
01096     next_token();  // eat 'template'
01097 
01098     eat_template_parms();
01099 
01100     // now all references to the template keyword and its parms are
01101     // gone fromthe stream
01102 
01103   }
01104 
01105   if(token.text_ == classKeyWord  || token.text_ == structKeyWord || token.text_ == unionKeyWord)
01106     return parse_class();
01107   else
01108   if(token.text_ == typedefKeyWord )
01109     return parse_typedef();
01110   else
01111   if(token.text_ == enumKeyWord )
01112     return parse_enum();
01113   else
01114   if(token.text_ == namespaceKeyWord )
01115     return parse_namespace();
01116   else
01117   if(token.text_ == usingKeyWord )
01118     return parse_using();
01119   else
01120   if(token.text_ == externKeyWord)
01121     return parse_extern();
01122   else
01123   if(token.text_ == friendKeyWord)
01124   {
01125     next_token(); // eat the friend keyword
01126 
01127     if((token.type_ == CPP_Token::aln) &&
01128        (token.text_ == classKeyWord ||
01129         token.text_ == structKeyWord
01130        )
01131       )
01132     {
01133       // prevent a misnaming of the scope caused by the
01134       // parsing of friend declarations.
01135 
01136       while(token.type_ != ';')
01137         next_token();
01138     }
01139 
01140     return parse_declaration(log_func_forward_decls);
01141 
01142   }
01143   else
01144   if(token.type_ == CPP_Token::aln &&
01145      token.text_[0] == 'p'         &&
01146        (token.text_ == publicKeyWord    ||
01147         token.text_ == privateKeyWord   ||
01148         token.text_ == protectedKeyWord
01149        )
01150     )
01151   {
01152     //
01153     // eat the public:, private:, and protected: syntax so as not
01154     // to confuse the 'public: typedef int x' as a variable definition.
01155     //
01156     next_token();
01157 
01158     if(token.type_ == ':')
01159       next_token();
01160 
01161     return false;
01162   }
01163   else
01164     return parse_funcvar(log_func_forward_decls);
01165 
01166 }
01167 
01168 
01169 static void log_symbol(string type, string name, string file, int line, bool include_scope)
01170 {
01171 
01172   if(!log_definitions)
01173      return;
01174 
01175   // the file parameter is unused unless we are doing grep style output
01176 
01177   if(name.size() != 0)
01178   {
01179     if(grep_style_output)
01180     {
01181       char buffer[40];
01182 
01183       sprintf(buffer, "%d", line);
01184 
01185       copy(file.begin(), file.end(), *output);
01186       *(*output)++ = ':';
01187 
01188       copy(buffer, buffer + strlen(buffer), *output);
01189       *(*output)++ = ':';
01190 
01191       *(*output)++ = ' ';
01192 
01193       copy(type.begin(), type.end(), *output);
01194       *(*output)++ = ' ';
01195 
01196       if(include_scope)
01197       {
01198         if(name.begin() != name.end() && *name.begin() != ':')
01199             copy(outer_scope.begin(), outer_scope.end(), *output);  // only symbols not in the global scope,
01200                                                                     // prefix them with their scope
01201       }
01202 
01203         
01204       copy(name.begin(), name.end(), *output);
01205 
01206 
01207       *output = copy_end_of_line(*output);
01208 
01209     }
01210     else
01211     {
01212 
01213       *(*output)++ = '-';
01214 
01215       copy(type.begin(), type.end(), *output);
01216       *(*output)++ = ' ';
01217 
01218       if(include_scope)
01219       {
01220         if(name.begin() != name.end() && *name.begin() != ':')
01221             copy(outer_scope.begin(), outer_scope.end(), *output);  // only symbols not in the global scope,
01222                                                                     // prefix them with their scope
01223       }
01224         
01225       copy(name.begin(), name.end(), *output);
01226       *(*output)++ = ' ';
01227 
01228       char buffer[40];
01229 
01230       sprintf(buffer, "%d", line);
01231 
01232       char *scan = buffer;
01233 
01234       while(*scan)
01235         *(*output)++ = *scan++;
01236 
01237       *output = copy_end_of_line(*output);
01238 
01239     }
01240   }
01241 
01242   cout.flush();
01243 
01244 }
01245 
01246 struct ScopeBinder
01247 //
01251 //
01252 {
01253   FunctionScope saved_scope; 
01254 
01255   ScopeBinder(string new_scope)
01258   {
01259     saved_scope = outer_scope;
01260     outer_scope.setScope(new_scope);
01261   }
01262 
01263   ~ScopeBinder()
01265   {
01266     outer_scope = saved_scope;
01267 
01268   }
01269 
01270 };
01271 
01272 
01273 static void parse_variables_defined(bool log_func_forward_decls=false,
01274                                     bool handling_typedefs=false
01275                                    )
01276 // parse and log the names of variables defined as part of a struct
01277 // declaration:
01278 //
01279 //   struct x { ... }  var1, *var2, function(..), array[...], ... ;
01280 {
01281 
01282   if(token.type_ != ';')
01283     parse_funcvar(log_func_forward_decls, handling_typedefs);
01284 
01285   return;
01286 
01287 }
01288 static void eat_function_parms(string *parmtext=0);
01289 
01290 
01291 
01292 
01293 static bool parse_class()
01294 //
01295 //  parse class, union, and struct declarations and keep track
01296 //  of the name of the 'scope' of each nested class
01297 //
01298 {
01299 
01300 
01301   string class_type = token.text_;
01302 
01303   next_token();  // eat the 'struct' or 'class' keyword
01304 
01305 
01306   string name = token.text_;  // save the class name
01307   string file = token.file_;
01308   int    line = token.line_;
01309   bool   maybeFunc = false;
01310 
01311   if(token.type_ == CPP_Token::der)
01312   {
01313       // handle this:
01314       //
01315       //  class ::SomeClass { ... }
01316 
01317       next_token();
01318 
01319       ASSUMED_TOKEN(CPP_Token::aln);
01320 
01321       name += token.text_;
01322       line  = token.line_;
01323 
01324   }
01325 
01326 
01327   if(token.type_ == '{')
01328   {
01329     name="unnamedstruct"; // handle struct { ... }
01330   }
01331   else
01332   {
01333       if(token.type_ == '[')
01334       {
01335         // someone in plain old c has defined int class[470];
01336         
01337         int depth=0;
01338         
01339         while(!eof())   // eat the contents of the arrays
01340         {
01341           if(token.type_ == '[')
01342             ++depth;
01343           else
01344           if(token.type_ == ']')
01345           {
01346             --depth;
01347             if(depth == 0)
01348               break;
01349           }
01350           next_token();
01351         }
01352 
01353         next_token();  // eat the closing ]
01354         
01355       }
01356       else if(token.type_ == '(')
01357       {
01358         // oops, someone in plain old C has done this:  int class() ...
01359         
01360         eat_function_parms();
01361         next_token();
01362       }
01363 
01364 
01365       if(token.type_ == ';' || token.type_ == ',' )
01366       {
01367           // we are not parsing a real class definition
01368           // but rather a plain old c style variable
01369           // declaration like 'int class;'
01370           next_token();
01371           return false;
01372       }
01373 
01374     ASSUMED_TOKEN(CPP_Token::aln);
01375    
01376     while(token.type_ == CPP_Token::aln)
01377     {
01378 
01379         next_token(); // should get here at least once
01380 
01381         if(token.type_ == CPP_Token::aln)
01382         {
01383 
01384            if(isJavaFile && token.text_ == extendsKeyWord)
01385               break;
01386 
01387            maybeFunc=true;
01388 
01389            // Assume we parsing something like this:
01390            //
01391            //   class MACRO ClassName {... }
01392            //               ^
01393            //
01394            // But we also have to support this:
01395            //
01396            //   class SomeClass  function() { return SomeClass(); }
01397            //                    ^
01398            //
01399            // And this
01400            // 
01401            //   class MACRO RealClassName { ... }
01402            //               ^
01403 
01404            name = token.text_;  // save the class name
01405            file = token.file_;
01406            line = token.line_;
01407         }
01408         else
01409         if( maybeFunc && token.type_ == ';' )
01410         {
01411            // log_symbol("variable", name, file, line);
01412            return false;
01413         }
01414 
01415     }
01416 
01417     while(token.type_ == CPP_Token::der)
01418     {
01419        // handle NAME::NAME::NAME... as the class name, as in
01420        //
01421        //   class CursorWindow::Dialog::Impl { ... }
01422 
01423        name += token.text_;
01424        line  = token.line_;
01425        next_token();
01426 
01427 
01428        if(token.type_ != CPP_Token::aln)
01429          break;
01430 
01431        name += token.text_;
01432        line  = token.line_;
01433        next_token();
01434 
01435     }
01436 
01437 
01438   }
01439 
01440 
01441   if(token.type_ == ';')
01442     return false;
01443 
01444   if(    (    token.type_ == CPP_Token::aln 
01445           &&  token.text_ != extendsKeyWord
01446           &&  token.text_ != implementsKeyWord
01447          )
01448      ||  token.type_ == '&'            
01449      ||  token.type_ == '*'
01450     )
01451   {
01452     // this is a variable declaration such as when some does this
01453     // in plain old c   typedef int class;   class &v1, *v2, x;
01454 
01455     parse_variables_defined();
01456 
01457     return false;
01458 
01459   }
01460 
01461 
01462   if(token.type_ == '(')
01463   {
01464       // we have something like this:
01465       //
01466       //   class something(...) ...
01467       
01468       eat_function_parms(); 
01469 
01470       if(token.type_ == ')')
01471       {
01472         next_token();
01473 
01474         if(maybeFunc)
01475         {
01476             if(token.type_ == '{' ||
01477                token.type_ == ';' ||
01478                (token.type_ == CPP_Token::aln && token.text_ == "const")
01479               )
01480             {
01481                 // we have something like this:
01482                 //
01483                 //  class  ClassName  functName() const { ... }
01484                 //                                ^     ^
01485                 // we are not defining a class, we are defining a function
01486 
01487                 while(   token.type_  != CPP_Token::eof
01488                       && token.type_  != ';'
01489                       && token.type_  != '{'
01490                       && token.type_  != '}'
01491                      )
01492                 {
01493                    next_token(); // skip till { or end of declaration
01494 
01495                 }
01496 
01497                 if(token.type_ == CPP_Token::eof)
01498                    return true;
01499 
01500                 if(token.type_ == '{')
01501                 {
01502 
01503                     outer_scope.setScopeMember(name);
01504                     outer_scope.setScopeFileInfo(file, line);
01505 
01506                     eat_function_body();
01507 
01508                     if(token.type_ == '}')
01509                        next_token();
01510                    
01511                 }
01512 
01513                 return false;
01514 
01515             }
01516              
01517         }
01518 
01519       }
01520     
01521       
01522       // assume we are seeing something like this:
01523       //
01524       //   class compiler_directive(parms)   name { ...
01525       
01526       if(token.type_ == CPP_Token::aln || token.type_ == CPP_Token::der)
01527       {
01528           name = token.text_;  // save the class name
01529           file = token.file_;
01530           line = token.line_;
01531       }
01532       
01533       if(token.type_ == CPP_Token::der)
01534       {
01535           // we are seeing this:
01536           //
01537           //   class directive(parms) ::name ...
01538 
01539           next_token();
01540 
01541           if(token.type_ == CPP_Token::aln)
01542           {
01543              name += token.text_;
01544              line  = token.line_;
01545           }
01546 
01547           next_token();
01548 
01549       }
01550       else
01551       {
01552         if(token.type_ == '<')
01553         {
01554            eat_template_parms();
01555            if(token.type_ == '>')
01556               next_token();
01557         }
01558 
01559 
01560         if(   token.type_ == '{' 
01561            || token.type_ == ':'
01562           )
01563           {
01564             // user has done something like this
01565             //   class function(parms) { .. }
01566             //                         ^
01567             // Normally, this is a syntax error, but if function
01568             // is really a macro that computes the class name, then, mabye
01569             // we want to not complain about it...
01570 
01571             if(token.type_ == ':')
01572               {
01573                 while(   token.type_ != ';'
01574                       && token.type_ != '{'
01575                       && token.type_ != CPP_Token::eof
01576                      )
01577                   {
01578                     next_token(); // consume tokens until { or ;
01579                   }
01580               }
01581 
01582 
01583           }
01584         else
01585         if(token.type_ == ';')
01586         {
01587              next_token();
01588              return false; // forward decl of badly formed class def
01589         }
01590         else
01591         if(token.type_ == CPP_Token::der)
01592         {
01593            // we are seeing someting like this:
01594            //   struct Macro(parms)<parms>  ::result<templateParms> : baseMacro(parms) {};
01595            //                               ^
01596            //
01597            // this is a multi-level specialization
01598 
01599            name=""; // we are going to reconstruct the name to refer to result
01600 
01601            while(token.type_ == CPP_Token::der)
01602            {
01603               next_token();
01604               
01605               ASSUMED_TOKEN(CPP_Token::aln);
01606 
01607               name+= token.text_;
01608               line = token.line_;
01609               file = token.file_;
01610 
01611               next_token();
01612            }
01613 
01614         }
01615         else
01616         {
01617             ASSUMED_TOKEN(CPP_Token::aln);
01618             next_token();
01619         }
01620 
01621 
01622       }
01623 
01624       while(token.type_ == CPP_Token::der)
01625       {
01626          name += token.text_;
01627          line  = token.line_;
01628          next_token();
01629 
01630          if(token.type_ != CPP_Token::aln)
01631             break;
01632 
01633          name += token.text_;
01634          line  = token.line_;
01635 
01636          next_token();
01637 
01638       }
01639 
01640       // At this point, we are here:
01641       //
01642       //   class directive(name) ::name::name::name ...
01643       //                                            ^
01644 
01645   }
01646   else
01647   if(token.type_ == '<')
01648   {
01649       // we have something like
01650       //
01651       //   template<> struct Class<instanceParms> { ... };
01652 
01653       eat_template_parms();
01654       if(token.type_ == '>')
01655          next_token();
01656      
01657   }
01658 
01659 
01660   // now we know that we really are in a normal c++ class
01661   // union, or struct declaration.  The classname now gets
01662   // the added to the scope.
01663 
01664 
01665   log_symbol(class_type, name, file, line);
01666 
01667   string newScope;
01668 
01669   static string colon_colon("::");
01670 
01671 
01672   if(name[0] != ':')
01673   {
01674       newScope = string(outer_scope) + name + colon_colon;
01675   }
01676   else
01677   {
01678      newScope = name + colon_colon;
01679   }
01680 
01681   ScopeBinder saved_scope(newScope);
01682 
01683   while(!eof() && token.type_ != '{' && token.type_ != ';' )
01684     next_token();
01685 
01686   if(token.type_ == ';')
01687   {
01688      next_token();
01689      return false;
01690   }
01691 
01692 
01693   if(eof())
01694     return true;
01695 
01696 
01697   next_token();
01698 
01699   while(token.type_ != '}')
01700   {
01701     if(token.type_ == '~')   // destructors mess up the parse_declaration logic
01702     {
01703       next_token();
01704       
01705       if(token.type_ == CPP_Token::aln)
01706       {
01707           static string tilde("~");
01708 
01709           token.text_ = tilde + token.text_;
01710       }
01711 
01712     }
01713 
01714     if(parse_declaration(true))
01715       return true;
01716   }
01717 
01718   next_token();
01719 
01720   outer_scope = saved_scope.saved_scope;
01721 
01722   parse_variables_defined();  // as part of this class declaration
01723 
01724   return false;
01725 
01726 }
01727 
01728 static void eat_type_name()
01729 //
01730 // parse and discard a typename -- particularly it handles
01731 // the unsigned type's bizare variants.
01732 //
01733 {
01734   while(token.type_ == CPP_Token::aln &&
01735         ( (token.text_ == typenameKeyWord) || (token.text_ == constKeyWord) )
01736        )
01737   {
01738     next_token();
01739   }
01740 
01741   if(token.text_ == "unsigned")
01742   {
01743     next_token();
01744 
01745     if(token.type_ == CPP_Token::aln &&
01746        (token.text_ == "int"    ||
01747         token.text_ == "short"  ||
01748         token.text_ == "long"   ||
01749         token.text_ == "char"
01750        )
01751       )
01752         next_token();
01753   }
01754   else
01755   {
01756     do
01757     {
01758       next_token();
01759     }
01760     while(token.type_ == CPP_Token::der);
01761 
01762   }
01763 
01764   eat_template_parms();  // if this is a template type name eat the parms
01765 
01766   while(   (!eof() && token.type_ == '*') 
01767         || (token.type_ == '&' )
01768         || (token.type_ == CPP_Token::aln && token.text_ == constKeyWord)
01769        )
01770   {
01771     next_token(); // eat normal type modifiers
01772   }
01773 
01774 }
01775 
01776 
01777 static bool parse_typedef()
01778 {
01779   next_token();  // eat the word 'typedef'
01780 
01781   if(token.text_ == classKeyWord || token.text_ == structKeyWord || token.text_ == unionKeyWord)
01782     return parse_class();
01783 
01784   if(token.text_ == enumKeyWord)
01785     return parse_enum();
01786 
01787   eat_type_name();
01788 
01789   parse_variables_defined(false, true);
01790                                         
01791 
01792   return false;
01793 }
01794 
01795 static bool parse_enum()
01796 //
01797 //  parse and log enumeration declarations
01798 //
01799 //    enum { ... } variables
01800 //    enum name {  ... } variables
01801 //    enum name variables
01802 //    enum [name] { name [=value,]... } [variables]
01803 //
01804 {
01805 
01806   next_token();  // eat 'enum'
01807 
01808   CPP_Token save = token;
01809 
01810   next_token();
01811 
01812   if(token.type_ == '{')
01813   {
01814     static string enum_type(enumKeyWord);
01815     static string enum_value("enumeration");
01816 
01817     log_symbol(enum_type, save.text_, save.file_, save.line_);
01818 
01819     // parse { name [=value, ...] }
01820 
01821     next_token();  // eat '{'
01822 
01823     while(token.type_ != '}')
01824     {
01825        if(token.type_ == CPP_Token::aln)
01826        {
01827          log_symbol(enum_value, token.text_, token.file_, token.line_);
01828         
01829          next_token();
01830         
01831          if(token.type_ == '=')
01832          {
01833            // eat initializer
01834         
01835            while(!eof() && token.type_ != ',' && token.type_ != '}')
01836              next_token();
01837         
01838          }
01839         
01840        }
01841        else
01842          next_token();
01843     }
01844 
01845     next_token();  // eat the closing '}'
01846 
01847   }
01848 
01849   parse_variables_defined();
01850 
01851   return false;
01852 }
01853 static bool parse_namespace()
01854   //
01855   // handle namespace [optional name] ;
01856   //
01857 {
01858   next_token();
01859 
01860   ScopeBinder old_scope(outer_scope);
01861 
01862   if(token.type_ == CPP_Token::aln)
01863   {
01864     // outer_scope += token.text_ + "::" ;
01865 
01866     string newName = string(outer_scope) + token.text_ + "::";
01867 
01868     outer_scope.setScope(newName);
01869 
01870     next_token();
01871   }
01872   else
01873   {
01874     outer_scope.setScope("<filescope>::");
01875   }
01876 
01877   if(token.type_ == '{')
01878   {
01879      next_token();
01880 
01881      while(!eof() && token.type_ != '}')
01882      {
01883        if(parse_declaration())
01884          return true;
01885      }
01886 
01887      next_token(); 
01888 
01889      return false;
01890 
01891   }
01892 
01893   return parse_declaration();
01894 
01895 }
01896 static bool parse_using()
01897 //
01898 // handle using namespace ;
01899 // or     using namespace::KeyWord ;
01900 //
01901 {
01902   next_token();  // eat 'using'
01903 
01904   bool is_namespace(false);
01905 
01906   if(token.text_ == namespaceKeyWord)
01907   {
01908     next_token();
01909     is_namespace = true;
01910   }
01911 
01912   CPP_Token tmp(token);
01913 
01914   tmp.text_.resize(0);
01915 
01916   while(!eof() && token.type_ != ';')
01917   {
01918     if(token.type_ == CPP_Token::aln ||
01919        token.type_ == CPP_Token::der
01920       )
01921     {
01922       tmp.text_ += token.text_;
01923     }
01924 
01925     next_token();
01926   }
01927 
01928   next_token(); // eat the semicolon
01929 
01930   static string used_symbol("used");
01931 
01932   if(!is_namespace && tmp.text_.size())
01933     log_symbol(used_symbol, tmp.text_, tmp.file_, tmp.line_);
01934 
01935   return false;
01936 }
01937 
01938 static void eat_parenthesis()
01939 //
01940 //  Assuming that the current token is placed immediately after
01941 //  an open parenthesis, consume text until a closing parentheis
01942 //  is found.  If the current token is a closing parenthesis, 
01943 //  it terminates immediately.
01944 //
01945 //  Nested parenthecals are skipped in the process.
01946 // 
01947 //  leading parenthesis should be in the stream.  trailing paren
01948 //  will be left in the stream
01949 {
01950   int depth=0;
01951 
01952   while(!eof())
01953   {
01954      if(token.type_ == '(')
01955        ++depth;
01956      else
01957      if(token.type_ == ')')
01958      {
01959        --depth;
01960        if(depth == 0)
01961        {
01962          break;
01963        }
01964      }
01965 
01966      next_token();
01967   }
01968 }
01969 
01970 static void eat_curly_block()
01971 //
01972 // ignore all tokens between matching {}'s.
01973 // (leaves '}' in the stream)
01974 //
01975 {
01976   int depth=0;
01977 
01978   while(!eof())
01979   {
01980      if(token.type_ == '{')
01981        ++depth;
01982      else
01983      if(token.type_ == '}')
01984      {
01985        --depth;
01986        if(depth == 0)
01987        {
01988           break;
01989        }
01990      }
01991 
01992      next_token();
01993   }
01994 
01995 //  cout << "EXITING CURLY BLOCK: " << token.file_ << " " << token.line_ << endl;
01996 
01997 }
01998 
01999 static void eat_till_semicolon()
02000   // and the semicolon too.
02001 {
02002     while(    token.type_ != CPP_Token::eof 
02003           &&  token.type_ != ';'   
02004           &&  token.type_ != '}'
02005           &&  token.type_ != '{'
02006 
02007           &&  !(    token.type_ == CPP_Token::aln 
02008                  && (     token.text_ == ifKeyWord 
02009                      ||   token.text_ == elseKeyWord
02010                      ||   token.text_ == whileKeyWord
02011                      ||   token.text_ == forKeyWord
02012                      ||   token.text_ == doKeyWord
02013                      ||   token.text_ == caseKeyWord
02014                      ||   token.text_ == defaultKeyWord
02015                      ||   token.text_ == switchKeyWord
02016                     )
02017                )
02018 
02019         )
02020     {
02021       next_token();
02022     }
02023 
02024     while(token.type_ == ';')
02025        next_token();
02026 }
02027 
02028 struct ElseifCounterInfo
02029 {
02030    int                    count_;
02031    FunctionScope::paths_t maxPaths_;   // largest encountered
02032    FunctionScope::paths_t basePaths_;  // start of if-else sequence
02033 
02034 };
02035 
02036 
02037 static bool process_statement(ElseifCounterInfo *);
02038 
02039 static void  eat_case_blocks()
02040    //
02041    //  Consume statements and tokens until we encounter end of file or }
02042    //
02043    //  return the count of case and default keywords found.
02044    //
02045    //  leave the path count of the scope set to the largest path count of any
02046    //  block.
02047    //
02048 {
02049 
02050    unsigned cases=0;
02051 
02052    FunctionScope::paths_t startPathCount   = outer_scope.paths();
02053    FunctionScope::paths_t largestPathCount = outer_scope.paths();
02054 
02055    while(    token.type_ != CPP_Token::eof
02056          &&  token.type_ != '}'
02057         )
02058    {
02059       // loop assumes that the current value of outer_scope.paths() is or has
02060       // been forced to be startPathCount
02061 
02062       if(    token.type_ == CPP_Token::aln
02063          &&  (   token.text_ == caseKeyWord
02064               || token.text_ == defaultKeyWord
02065              )
02066         )
02067       {
02068          ++cases;
02069          
02070          if( outer_scope.paths() > largestPathCount )
02071          {
02072             largestPathCount = outer_scope.paths();
02073          }
02074 
02075          outer_scope.setPaths( startPathCount );
02076 
02077          while(    token.type_ != ':' 
02078                &&  token.type_ != '}'
02079                &&  token.type_ != CPP_Token::eof
02080               )
02081          {
02082             next_token();
02083          }
02084 
02085          if(token.type_ == ':')
02086             next_token();
02087 
02088          // we are past the 'case id :' or the 'default :' part and
02089          // ready to process statements.
02090 
02091          while(    token.type_ != CPP_Token::eof
02092                &&  token.type_ != '}'
02093                && !(
02094                        token.type_ == CPP_Token::aln
02095                     && (
02096                            token.text_ == caseKeyWord
02097                         || token.text_ == defaultKeyWord
02098                        )
02099                    )
02100               )
02101          {
02102             process_statement(0);  // process statements that don't affect the 
02103                                   // logic of the switch clause we are in
02104          }
02105               
02106          
02107 
02108       }
02109       else
02110          process_statement(0); // ignoring 'return' flag here.  Don't see how we could use it
02111                               // yet
02112 
02113    }
02114 
02115    if(largestPathCount > outer_scope.paths())
02116    {
02117      outer_scope.setPaths(largestPathCount);
02118    }
02119    
02120    outer_scope.multiplyPaths(cases);
02121 
02122 }
02123 
02124 static bool process_statement(ElseifCounterInfo *elseifCounter)
02125    // and remove the trailing terminal:  ; or }
02126    //
02127    // return true only if the last executable thing in the statement
02128    // was a return directive.  For non-block statements, of course this
02129    // simply means that the statement was a return statement.  For block
02130    // statements, "{...}", it means that the last statement before the }
02131    // was a return.
02132 {
02133 
02134     // Warning:  do not return early from this function, always drop through
02135     // the bottom!  
02136 
02137     bool returnWasLastStatement = false;
02138 
02139     if( token.type_ == '{')
02140     {
02141         next_token();
02142 
02143         while(    token.type_ != CPP_Token::eof
02144               &&  token.type_ != '}'
02145              )
02146         {
02147            returnWasLastStatement = process_statement(0);
02148         }
02149 
02150         if(token.type_ == '}')
02151             next_token();
02152 
02153         // we have consumed leading and trailing } and all statements within.
02154 
02155     }
02156     else
02157     if(token.type_ == CPP_Token::aln)
02158     {
02159 
02160        if( token.text_ == ifKeyWord )
02161        {
02162           outer_scope.addStatement();
02163 
02164           ElseifCounterInfo myInfo;
02165 
02166           myInfo.count_     = 0;
02167           myInfo.maxPaths_  = outer_scope.paths();
02168           myInfo.basePaths_ = outer_scope.paths();
02169 
02170           if(elseifCounter == 0 || (!collapse_elseif) )
02171           {
02172              elseifCounter = &myInfo;
02173           }
02174 
02175           ++elseifCounter->count_;
02176 
02177           next_token();
02178 
02179           eat_parenthesis();
02180 
02181           if(token.type_ == ')')
02182             next_token();
02183 
02184           bool oneSideReturned = false;
02185 
02186           // handle the if-clause
02187 
02188           if(process_statement(elseifCounter))
02189              oneSideReturned = true;
02190 
02191           if( outer_scope.paths() > elseifCounter->maxPaths_)
02192              elseifCounter->maxPaths_ = outer_scope.paths();
02193 
02194           outer_scope.setPaths(elseifCounter->basePaths_);
02195 
02196           // if we have an else clause, handle it
02197           
02198           if(   token.type_ == CPP_Token::aln 
02199              && token.text_ == elseKeyWord
02200             )
02201           {
02202              next_token();
02203           
02204              if(process_statement(elseifCounter))
02205                 oneSideReturned = true;
02206 
02207              if(outer_scope.paths() > elseifCounter->maxPaths_)
02208                 elseifCounter->maxPaths_ = outer_scope.paths();
02209 
02210           }
02211 
02212           outer_scope.setPaths(elseifCounter->maxPaths_);
02213 
02214           if(elseifCounter->count_ == 1 )
02215           {
02216              // this is not an if-else-if sequence
02217 
02218              if(!oneSideReturned)
02219                 outer_scope.doublePaths();
02220           }
02221           else
02222           if(elseifCounter->count_)
02223           {
02224             // we have not yet handled this elseifCounter's count of paths
02225 
02226              outer_scope.setPaths( (1 + elseifCounter->count_) * outer_scope.paths() );
02227 
02228              elseifCounter->count_ = 0; // suppress further processing thereof.
02229           }
02230 
02231        }
02232        else
02233        if(token.text_ == elseKeyWord)
02234        {
02235          // we should never get here, it indicates a random else in
02236          // the code
02237 
02238          cerr << "Error:  "
02239               << token.file_ << ":" << token.line_
02240               << ": unexpected else keyword" 
02241               << endl;
02242 
02243 
02244          next_token();
02245 
02246          returnWasLastStatement = process_statement(0);
02247 
02248        }
02249        else
02250        if(    token.text_ == whileKeyWord 
02251           ||  token.text_ == forKeyWord
02252          )
02253        {
02254           outer_scope.addStatement();
02255           outer_scope.doublePaths();
02256 
02257 
02258           next_token(); // skip while, for, or switch token
02259 
02260           eat_parenthesis();
02261 
02262           if(token.type_ == ')' )
02263           {
02264 
02265               next_token();  // eat the trailing parenthesis
02266               
02267           }
02268 
02269           process_statement(0);  // does not affect returnWasLastStatement
02270                                 // because these are looping constructs.
02271 
02272        }
02273        else
02274        if( token.text_ == doKeyWord)
02275        {
02276           next_token();
02277 
02278           process_statement(0); // does not affect returnWasLastStatement
02279 
02280           if(    token.type_ == CPP_Token::aln
02281              &&  token.text_ == whileKeyWord
02282             )
02283           {
02284              outer_scope.addStatement();
02285 
02286              // we have your standard do...while construct folks, nothing to see, move a long, move a long
02287 
02288              next_token(); // eat the while
02289 
02290              eat_parenthesis();
02291 
02292              if( token.type_ == ')')
02293                next_token();
02294 
02295              outer_scope.addStatement();
02296              eat_till_semicolon(); // and the semicolon too
02297              outer_scope.doublePaths();
02298 
02299           }
02300        }
02301        else
02302        if( token.text_ == switchKeyWord  )
02303        {
02304            next_token();  // eat 'switch'
02305 
02306            if(token.type_ != '(')
02307            {
02308              cerr << "Error:  "
02309                   << token.file_ << ":" << token.line_
02310                   << ": expected '(' after 'switch' " 
02311                   << endl;
02312            }
02313            else
02314            {
02315 
02316              eat_parenthesis();
02317 
02318              if( token.type_ == ')')
02319                next_token();
02320 
02321              outer_scope.addStatement();
02322 
02323              if(token.type_ != '{')
02324              {
02325                  cerr << "Error:  "
02326                       << token.file_ << ":" << token.line_
02327                       << ": expected '{' after 'switch()'" 
02328                       << endl;
02329 
02330              }
02331              else
02332              {
02333                  next_token();  // eat the leading{
02334 
02335                  eat_case_blocks(); // leave trailing } in the token
02336 
02337                  if(token.type_ != '}')
02338                  {
02339                      cerr << "Error:  "
02340                           << token.file_ << ":" << token.line_
02341                           << ": expected '}' after 'switch() { ... case ... '" 
02342                           << endl;
02343                  }
02344                  else
02345                  {
02346                     next_token();  // eat the trailing }
02347                  }
02348 
02349 
02350              }
02351 
02352            }
02353            
02354        }
02355        else
02356        if(   token.text_ == caseKeyWord
02357           || token.text_ == defaultKeyWord
02358          )
02359        {
02360          cerr << "Error:  "
02361               << token.file_ << ":" << token.line_
02362               << ": expected keyword: " 
02363               << token.text_
02364               << endl;
02365 
02366          while(token.type_ != CPP_Token::eof
02367                && token.type_ != ':'
02368               )
02369          {
02370              next_token(); 
02371          }
02372 
02373          if(token.type_ == ':')
02374            next_token();
02375 
02376        }
02377        else
02378        if(token.text_ == catchKeyWord )
02379        {
02380           FunctionScope::paths_t basePaths = outer_scope.paths();
02381           FunctionScope::paths_t maxPaths  = outer_scope.paths();
02382 
02383           do
02384           {
02385              outer_scope.setPaths(basePaths);
02386 
02387              outer_scope.addStatement();  // catch is a statement because
02388                                           // code gets generated.
02389 
02390              next_token();  //eat 'catch'
02391 
02392              if(token.type_ != '(')
02393              {
02394                 
02395                  cerr << "Error:  "
02396                       << token.file_ << ":" << token.line_
02397                       << ": unexpected token, '" 
02398                       << token.text_
02399                       << "'"
02400                       << endl;
02401 
02402                  break;
02403              }
02404 
02405              eat_parenthesis();
02406 
02407              if( token.type_ == ')')
02408                next_token();
02409 
02410              process_statement(0); // should be a curly for valid
02411                                    // syntax, but we only claim to work
02412                                    // on valid syntax...
02413 
02414 
02415              if(outer_scope.paths() > maxPaths)
02416                 maxPaths = outer_scope.paths();
02417 
02418           }
02419           while(    token.type_ == CPP_Token::aln
02420                 &&  token.text_ == catchKeyWord
02421                );
02422 
02423           outer_scope.setPaths(maxPaths);
02424        }
02425        else
02426        if(token.text_ == tryKeyWord)
02427        {
02428           next_token();  // eat the try keyword
02429 
02430           if( token.type_ != '{')
02431           {
02432              cerr << "Error:  "
02433                   << token.file_ << ":" << token.line_
02434                   << ": unexpected token, '" 
02435                   << token.text_
02436                   << "', expected '{'"
02437                   << endl;
02438 
02439           }
02440           else
02441           {
02442              process_statement(0); // eat the try clause
02443 
02444              while(   token.type_ == CPP_Token::aln 
02445                    && token.text_ == catchKeyWord
02446                   )
02447              {
02448                  process_statement(0); // eat the catch clause
02449              }
02450 
02451           }
02452          
02453        }
02454        else
02455        {
02456           outer_scope.addStatement();
02457 
02458           if(   collapse_return
02459              && token.type_ == CPP_Token::aln
02460              && (    token.text_ == returnKeyWord
02461                  ||  token.text_ == throwKeyWord
02462                  ||  (   returnSynonyms.find(token.text_) 
02463                       != returnSynonyms.end() 
02464                      )
02465                 )
02466             )
02467           {
02468             returnWasLastStatement = true;
02469           }
02470 
02471           // not call outer_scope.doublePaths() here.  Instead, we need to count the number
02472           // of the cases/defaults in this switch statement and multipley the path count by
02473           // that number rather than using the number 2 and most certainly, do not double
02474           // the path count on ever case/default.
02475 
02476           eat_till_semicolon(); // and the semicolon too
02477        }
02478 
02479     }
02480     else
02481     {
02482       outer_scope.addStatement();
02483       eat_till_semicolon(); // and the semicolon too
02484     }
02485 
02486     return returnWasLastStatement;
02487 }
02488 
02489 
02490 static void eat_function_body()
02491 //
02492 // process all statements in a function body.  The trailing } as the current
02493 // token when the function exits.
02494 //
02495 {
02496   string const &funcName = outer_scope.scopeMemberValue();
02497 
02498   if(    funcName == ifKeyWord
02499      ||  funcName == whileKeyWord
02500      ||  funcName == forKeyWord
02501      ||  funcName == switchKeyWord
02502      ||  funcName == catchKeyWord
02503     )
02504   {
02505      cerr << outer_scope.scopeFile() 
02506           << ":" 
02507           << outer_scope.scopeLine() 
02508           << ":  mismatched { ... } earlier in function"
02509           << "\n   check for #if blocks with unmatched { or }"
02510           << endl;
02511   }
02512 
02513 
02514   FunctionScope savedscope(outer_scope);
02515 
02516   outer_scope.resetCounters();
02517 
02518   next_token();  // we would not get here if we were not already sitting on a {
02519 
02520   while(    token.type_ != CPP_Token::eof
02521         &&  token.type_ != '}'
02522        )
02523   {
02524       process_statement(0);
02525   }
02526 
02527   if(token.type_ == '}')
02528      next_token();
02529 
02530   outer_scope.setScope( string(savedscope) );
02531   outer_scope.setScopeMember( savedscope.scopeMemberValue() );
02532   outer_scope.setScopeFileInfo(savedscope.scopeFile(), savedscope.scopeLine() );
02533 
02534   if(print_functions)
02535       cout << outer_scope << endl;
02536 
02537   if(print_summary)
02538       summarize();
02539 
02540   while(    token.type_ == CPP_Token::aln
02541         &&  token.text_ == catchKeyWord
02542        )
02543   {
02544      eat_till_semicolon(); // will stop on the leading {
02545 
02546      process_statement(0);  // at the catch body
02547 
02548   }
02549 
02550 }
02551 
02552 
02553 
02554 
02555 static void eat_function_parms(string *parmtext)
02556 //
02557 // ignore all tokens between matching ()'s.
02558 // (leaves ')' in the stream).  The first alphanumeric
02559 // token between the paren's is stuck in parmtext if
02560 // non-zero
02561 //
02562 {
02563   int depth=0;
02564 
02565   while(!eof())
02566   {
02567      if(token.type_ == '(')
02568        ++depth;
02569      else
02570      if(token.type_ == ')')
02571      {
02572        --depth;
02573        if(depth == 0)
02574          return;
02575      }
02576 
02577      if(parmtext && depth == 1 && token.type_ == CPP_Token::aln)
02578      {
02579        *parmtext = token.text_;
02580        parmtext = 0;
02581      }
02582 
02583      next_token();
02584   }
02585 
02586 }
02587 
02588 static void eat_template_parms()
02589   // parse and discard template parms
02590   //
02591   // that is:   < stuff, more_stuff< a, b, c>, ... >
02592   // Also handles:
02593   //
02594   //   class templatename< ... >
02595   //
02596 {
02597 
02598   int depth=0;
02599 
02600   if(token.type_ == CPP_Token::aln &&
02601      token.text_ == classKeyWord
02602     )
02603   {
02604     next_token();  // eat 'class' keyword
02605     next_token();  // eat template specialization name
02606   }
02607 
02608   if(token.type_ != '<')
02609     return;
02610 
02611   while(!eof())
02612   {
02613      if(    token.type_ == ';' 
02614         ||  token.type_ == '{'
02615         ||  token.type_ == '}'
02616        )
02617      {
02618          break;
02619      }
02620 
02621      if(token.type_ == '<')
02622        ++depth;
02623      else
02624      if(token.type_ == '>')
02625      {
02626        --depth;
02627        if(depth == 0)
02628          return;
02629      }
02630 
02631      next_token();
02632   }
02633 
02634 
02635 }
02636 
02637 static bool parse_funcvar(bool log_forward_func_declarations,
02638                           bool handling_typedefs
02639                          )
02640 //
02641 // parse all function and variable declarations -- and handle
02642 // any leftover trash that occurs whenever you are parsing
02643 // something that is not strictly syntactically correct.
02644 //
02645 // So instead of defining a function like this:
02646 //
02647 //    int function(parms) { body }
02648 //
02649 // This code will accept
02650 //
02651 //    trash ... trash int trash trash ... function(parms) trash .. trash { ... }
02652 //
02653 // It will also parse handle things like this:
02654 //
02655 //    trash struct { members } varname ;
02656 //
02657 // The 'trash' that is excepted includes pretty much any random tokens
02658 // and when '[', '{', the text up to the corresponding closing token
02659 // is simply ignored.
02660 //
02661 {
02662   // scan tokens until you first encounter one of
02663   //  (, [, {, struct/class/union/template or ;
02664   // that tells you that you are defining.
02665 
02666   CPP_Token tmp; // save token just before terminator
02667 
02668   if(token.text_ == staticKeyWord)
02669     next_token();
02670 
02671   do
02672   {
02673 
02674 
02675 
02676     if(token.type_ == '{')
02677     {
02678       // some bizarre code fragment we misunderstood
02679 
02680       eat_curly_block();
02681 
02682       if(token.type_ == '}')
02683           next_token();  // eat the trailing '}'
02684 
02685 
02686       if(token.type_ == ',')
02687       {
02688           static string unknown_type("unknown");
02689         
02690           next_token();
02691           log_symbol(unknown_type, tmp.text_, tmp.file_, tmp.line_);
02692           return parse_funcvar();
02693       }
02694       return false;
02695 
02696     }
02697 
02698     if(token.type_ == CPP_Token::aln)
02699     {
02700       if(token.text_ == structKeyWord   ||
02701          token.text_ == classKeyWord    ||
02702          token.text_ == unionKeyWord    ||
02703          token.text_ == templateKeyWord ||
02704          token.text_ == externKeyWord
02705         )
02706         return parse_declaration();
02707 
02708       tmp = token;
02709 
02710       next_token();
02711 
02712       if(token.type_ == '<')
02713       {
02714         eat_template_parms();
02715       }
02716 
02717     }
02718     else
02719     if(token.type_ == '=')
02720     {
02721       // scoop up and ignore a variable initializer
02722 
02723       while(token.type_ != ',' &&
02724             token.type_ != ';' &&
02725             !eof()
02726            )
02727       {
02728         if(token.type_ == '{')
02729           eat_curly_block();
02730         else
02731         if(token.type_ == '(')
02732           eat_function_parms();
02733         
02734         next_token();
02735       }
02736 
02737 
02738     }
02739     else
02740     if(token.type_ == '>')
02741     {
02742        next_token();
02743 
02744        if(token.type_ == ';')
02745        {
02746           next_token();
02747           return false;   // handle extern template<parms>; which forces instantiation of a signature
02748        }
02749     }
02750     else
02751       next_token();
02752 
02753     if(token.type_ == '~')
02754     {
02755       next_token();
02756 
02757       if(token.type_ == CPP_Token::aln)
02758       {
02759         static string tilde("~");
02760         
02761         token.text_ = tilde + token.text_;
02762       }
02763 
02764     }
02765 
02766     while( token.type_ == CPP_Token::der) // ::
02767     {
02768       next_token();
02769 
02770       if(token.type_ == '~')
02771       {
02772         next_token();
02773 
02774         if(token.type_ == CPP_Token::aln)
02775         {
02776           static string tilde("~");
02777         
02778           token.text_ = tilde + token.text_;
02779         }
02780 
02781       }
02782 
02783       if(token.type_ != CPP_Token::aln)
02784         break;
02785         
02786       tmp.text_ += "::";
02787       tmp.text_ += token.text_;
02788       tmp.line_  = token.line_;
02789 
02790       next_token();
02791     }
02792 
02793   }
02794   while(!eof()             &&
02795         token.type_ != '(' &&
02796         token.type_ != ';' &&
02797         token.type_ != ',' &&
02798         token.type_ != '['
02799        );
02800 
02801   bool is_function= (token.type_ == '(');
02802 
02803   string parmtext;
02804   string file = token.file_;
02805   int    line = token.line_;
02806 
02807 
02808   if(token.type_ == '(')
02809   {
02810     eat_function_parms(&parmtext);
02811     next_token();
02812   }
02813   else
02814   if(token.type_ == '[')
02815   {
02816      // eat until matching ']' -- and eat it too
02817 
02818      int depth = 0;
02819 
02820      while(!eof())
02821      {
02822        if(token.type_ == '[')
02823          ++depth;
02824        else
02825        if(token.type_ == ']')
02826        {
02827          --depth;
02828          if(depth == 0)
02829            break;
02830        }
02831 
02832        next_token();
02833 
02834      }
02835 
02836      next_token();
02837 
02838      // we should see either ';' or ',' as the token there
02839 
02840      if( token.type_ == '=')
02841      {
02842         // consume array initializer body:
02843         //
02844         //   int junk[14] = { 1, 2,  }   ;
02845         //                  ^^^^^^^^^^ 
02846 
02847         next_token();
02848 
02849         if(token.type_ == '{')
02850         {
02851             eat_curly_block();
02852 
02853             if(token.type_ == '}')
02854               next_token();
02855            
02856         }
02857 
02858      }
02859 
02860 
02861   }
02862 
02863 
02864   if(token.type_ == '(')
02865   {
02866     // we are dealing with something like a pointer to a function
02867     // where the following syntax was found:
02868     //
02869     //  int (*f)(parms) {}
02870 
02871     tmp.text_ = parmtext;
02872     tmp.file_ = file;
02873     tmp.line_ = line;
02874 
02875     eat_function_parms(&parmtext);
02876     next_token();
02877 
02878   }
02879   else
02880   if(token.type_ == ':')
02881   {
02882     // ignore the base class initalizers in a constructor's name
02883 
02884     while(!eof() && token.type_ != '{')
02885       next_token();
02886 
02887   }
02888 
02889   switch(token.type_)
02890   {
02891     case CPP_Token::eof:
02892          return true;
02893 
02894     case ';':
02895 
02896          {
02897            string variable_type("variable");
02898            string typename_type("typename");
02899            string function_type("function");
02900         
02901            if(!is_function || log_forward_func_declarations)
02902             log_symbol( handling_typedefs 
02903                          ? typename_type 
02904                          : (is_function 
02905                             ? function_type
02906                             : variable_type
02907                            ),
02908                        tmp.text_,
02909                        tmp.file_,
02910                        tmp.line_
02911                       );
02912         
02913            next_token();
02914          }
02915          return false;
02916         
02917     case ',':
02918 
02919          {
02920            static string variable_type("variable");
02921         
02922            if(!is_function)
02923             log_symbol(variable_type, tmp.text_, tmp.file_, tmp.line_);
02924         
02925            next_token();
02926            return parse_funcvar();
02927          }
02928     default:
02929 
02930          {
02931            static string function_type("function");
02932         
02933            outer_scope.setScopeMember(tmp.text_);
02934            outer_scope.setScopeFileInfo(tmp.file_, tmp.line_);
02935 
02936            log_symbol(function_type, tmp.text_, tmp.file_, tmp.line_);
02937          }
02938         
02939          // handle a goofy g++ variant syntax
02940         
02941            if(token.text_ == "return")
02942            {
02943              while(!eof() && token.type_ != '{')
02944                next_token();
02945            }
02946         
02947          // end goofy g++ variant syntax
02948 
02949          while(!eof() && 
02950                token.type_ != ';' && 
02951                token.type_ != '{' &&
02952                token.type_ != ','
02953               )
02954          {
02955            if(token.type_ == '(')
02956            {
02957              string trash;
02958              eat_function_parms(&trash);
02959              next_token();
02960            }
02961            else
02962              next_token();
02963          }
02964 
02965          if(token.type_ == '{')
02966          {
02967            eat_function_body();
02968 
02969         
02970            while(token.type_ == ':')
02971            {
02972              // presume we are passing a class definition with #if's in it
02973              // like this:
02974              //
02975              //   class X
02976              //   {
02977              //     X()
02978              //     #if 1
02979              //        : base1() {}
02980              //     #else
02981              //        : base2() {}   <-- the parser is now sitting at the ':'
02982              //     #endif
02983         
02984              while(token.type_ != '{')
02985                next_token();
02986         
02987              eat_function_body();
02988         
02989 
02990         
02991         
02992            }
02993         
02994          }
02995          else
02996              if(token.type_ == ',')
02997              {
02998                  next_token();
02999                  return parse_funcvar();
03000              }
03001           else
03002                next_token();
03003         
03004          break;
03005         
03006   }
03007 
03008 
03009   return false;
03010 }
03011 
03012 static bool parse_extern()
03013 //
03014 //  parse extern variables and extern code definitions:
03015 //
03016 //    extern type varname;
03017 //    extern type funcname;
03018 //    extern "c" declaration;
03019 //    extern "c" { declarations ... }
03020 //
03021 {
03022   next_token();  // eat 'extern'
03023 
03024   if(token.type_ == '"')
03025   {
03026      next_token();
03027 
03028      if(token.type_ != '{')
03029      {
03030        return parse_declaration();
03031      }
03032 
03033      next_token();
03034 
03035      while(token.type_ != '}')
03036      {
03037        if(parse_declaration())
03038          return true;
03039      }
03040 
03041      next_token();
03042 
03043   }
03044   else
03045   {
03046      return parse_declaration();
03047   }
03048 
03049   return false;
03050 }
03051 
03052 
03053 static void printInfo()
03054    // print a commentary on the algorithm.
03055 {
03056 
03057    static char const *info[] =
03058    {
03059        "cca.exe:  print McCabe style Cyclomatic Complexity Analysis for C++",
03060        "",
03061        "    Copyright 2002, 2010, Lowell Boggs Jr.",
03062        "",
03063        "Overview:",
03064        "",
03065        "   cca.exe reads C++ source files and produces an cyclomatic",
03066        "   number for each function defined in that file.  It works on",
03067        "   both \".cpp\" and \".h\" files.",
03068        "",
03069        "   The cyclomatic complexity measure is documented on wikipedia.",
03070        "   As a general rule, a cyclomatic complexity greater than 10",
03071        "   indicates that a function will be very difficult to test.",
03072        "   If possible, such functions are candidates for refactoring.",
03073        "",
03074        "   McCabe originally described his metric based on compiler internal",
03075        "   concepts:  that of basic program blocks.  A basic block is a",
03076        "   chunk of code with no conditionals statements.  Conditionals",
03077        "   break up functions into multiple blocks.  Transitions between",
03078        "   the blocks are key components McCabe's calculation.",
03079        "",
03080        "   This program however uses a different strategy for the calculation",
03081        "   that is based on features of the C++ language itself:  grammar",
03082        "   and syntax.  By default, this program also relaxes McCabe's",
03083        "   calculation a bit to account for common programming strategies",
03084        "   that exaggerate the McCabe metric (at least in the author's",
03085        "   opinion.  This relaxation can be turned off, see \"-m\".",
03086        "",
03087        "   This program does not count basic blocks and their transitions",
03088        "   but rather it counts conditionals.  Every function begins with",
03089        "   an NPATH (number of paths) of 1.  The following statement types",
03090        "   double the NPATH for the function:",
03091        "",
03092        "       if",
03093        "       while",
03094        "       for",
03095        "       do-while",
03096        "",
03097        "   The base-2 logarithm of the path count is printed as the",
03098        "   cyclomatic complexity for the function.",
03099        "",
03100        "   Switch statements don't double the complexity, but rather they",
03101        "   multiply it by the count of case/default clauses.",
03102        "",
03103        "   Nested statements are accounted for in the metric.  When dealing",
03104        "   with if-statements and switch-statements, the statement itself",
03105        "   affects the metric, but only the largest nested section affects",
03106        "   it.  This seems to make sense given that only one clause will",
03107        "   actually execute.  The section with the largest path count is",
03108        "   chosen.",
03109        "",
03110        "   By default, two extensions to McCabe's analysis are turned on:",
03111        "",
03112        "     *  else-if collapsing",
03113        "     *  return/throw collapsing",
03114        "",
03115        "   See options -m, -e, and -r to turn these features off.",
03116        "",
03117        "   When else-if combinations occur, only one of the alternatives",
03118        "   will actually execute.  Thus, having each if-statement in the",
03119        "   group double the McCabe metric unfairly inflates the path",
03120        "   count.  If an if-clause or an else-clause has a return statement",
03121        "   in it, and if that branch is executed, then the rest of the",
03122        "   statements in the function are short-circuited.  Thus the",
03123        "   special case of \"if(x) return; else {...} ;\" is treated as",
03124        "   if the if-statement does not increase the path count.  The",
03125        "   contents of the else-clause WILL still affect the path count.",
03126        "",
03127        "   The '-R returnSynonym' options let you define synonyms for",
03128        "   the return statement.  Here's an example use:",
03129        "      -R exit -R THROW_EXCEPTION",
03130        "   When used, any -R defined symbol will be treated as if it were a",
03131        "   return statement unless -r or -m is used.",
03132        "",
03133        "   try-catch blocks are processed as follows:",
03134        "     * The try blocks is treated as if were normal code.",
03135        "     * The catch blocks are treated as alternative sections.",
03136        "       Only the largest path found in any of the catch blocks",
03137        "       is kept, so that the total impact on the number of",
03138        "       execution paths is:",
03139        "          impact(try) * largest( impact(any catch) )",
03140        "",
03141        ""
03142 
03143    };
03144 
03145    for(unsigned i = 0; i < sizeof(info) / sizeof(char const *); ++i)
03146    {
03147       cout << info[i] << endl;
03148    }
03149 
03150 }
Generated on Wed Feb 29 22:50:03 2012 for CXXUtilities by  doxygen 1.6.3