00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
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");
00166 static string implementsKeyWord ("implements");
00167
00168 static void printInfo();
00169 static void printSummary();
00170
00171 static set<string> returnSynonyms;
00172
00173 static bool isJavaFile = false;
00174
00175 int main(int argc, char **argv, char **environ)
00176 {
00177
00178 # ifdef _MSC_VER
00179
00180
00181
00182
00183 cout.flush();
00184
00185 setmode(1, O_BINARY);
00186
00187 # endif
00188
00189 output = new ostreambuf_iterator<char>(cout);
00190
00191
00192
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
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
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
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 ¤t = *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());
00399
00400
00401
00402
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
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
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
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
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
00609
00610 if( f == g)
00611 return;
00612
00613 if( *f != 'd')
00614 {
00615 return ifHandler(define, file, line);
00616
00617 return;
00618 }
00619
00620
00621 while(f != g && *f != ' ') ++f;
00622
00623
00624
00625 while(f != g && (*f == ' ' || *f == '\n') ) ++f;
00626
00627 if(f == g)
00628 return;
00629
00630
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
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
00658
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;
00708
00709
00710 private:
00711
00712
00713
00714 string scopeName;
00715 string scopeMember;
00716 string scopeFile_;
00717 int scopeLine_;
00718
00719 int statements_;
00720
00721 paths_t paths_;
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
00747
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
00811
00812
00813 paths_ = paths_t(-1);
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
00827
00828
00829 paths_ = paths_t(-1);
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;
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
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
00975
00976
00977
00978
00979
00980
00981
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
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
01008
01009 {
01010 stream = &s;
01011
01012 for(next_token(); !eof(); )
01013 {
01014 outer_scope.setScope("::");
01015
01016 if(parse_declaration())
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
01039
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_ == ';' )
01057 {
01058 next_token();
01059 return false;
01060 }
01061
01062 if(token.type_ == '{')
01063 {
01064
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_ == '=')
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
01090
01091
01092 if(token.text_ == templateKeyWord)
01093 {
01094
01095
01096 next_token();
01097
01098 eat_template_parms();
01099
01100
01101
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();
01126
01127 if((token.type_ == CPP_Token::aln) &&
01128 (token.text_ == classKeyWord ||
01129 token.text_ == structKeyWord
01130 )
01131 )
01132 {
01133
01134
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
01154
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
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);
01200
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);
01222
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
01277
01278
01279
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
01296
01297
01298 {
01299
01300
01301 string class_type = token.text_;
01302
01303 next_token();
01304
01305
01306 string name = token.text_;
01307 string file = token.file_;
01308 int line = token.line_;
01309 bool maybeFunc = false;
01310
01311 if(token.type_ == CPP_Token::der)
01312 {
01313
01314
01315
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";
01330 }
01331 else
01332 {
01333 if(token.type_ == '[')
01334 {
01335
01336
01337 int depth=0;
01338
01339 while(!eof())
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();
01354
01355 }
01356 else if(token.type_ == '(')
01357 {
01358
01359
01360 eat_function_parms();
01361 next_token();
01362 }
01363
01364
01365 if(token.type_ == ';' || token.type_ == ',' )
01366 {
01367
01368
01369
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();
01380
01381 if(token.type_ == CPP_Token::aln)
01382 {
01383
01384 if(isJavaFile && token.text_ == extendsKeyWord)
01385 break;
01386
01387 maybeFunc=true;
01388
01389
01390
01391
01392
01393
01394
01395
01396
01397
01398
01399
01400
01401
01402
01403
01404 name = token.text_;
01405 file = token.file_;
01406 line = token.line_;
01407 }
01408 else
01409 if( maybeFunc && token.type_ == ';' )
01410 {
01411
01412 return false;
01413 }
01414
01415 }
01416
01417 while(token.type_ == CPP_Token::der)
01418 {
01419
01420
01421
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
01453
01454
01455 parse_variables_defined();
01456
01457 return false;
01458
01459 }
01460
01461
01462 if(token.type_ == '(')
01463 {
01464
01465
01466
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
01482
01483
01484
01485
01486
01487 while( token.type_ != CPP_Token::eof
01488 && token.type_ != ';'
01489 && token.type_ != '{'
01490 && token.type_ != '}'
01491 )
01492 {
01493 next_token();
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
01523
01524
01525
01526 if(token.type_ == CPP_Token::aln || token.type_ == CPP_Token::der)
01527 {
01528 name = token.text_;
01529 file = token.file_;
01530 line = token.line_;
01531 }
01532
01533 if(token.type_ == CPP_Token::der)
01534 {
01535
01536
01537
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
01565
01566
01567
01568
01569
01570
01571 if(token.type_ == ':')
01572 {
01573 while( token.type_ != ';'
01574 && token.type_ != '{'
01575 && token.type_ != CPP_Token::eof
01576 )
01577 {
01578 next_token();
01579 }
01580 }
01581
01582
01583 }
01584 else
01585 if(token.type_ == ';')
01586 {
01587 next_token();
01588 return false;
01589 }
01590 else
01591 if(token.type_ == CPP_Token::der)
01592 {
01593
01594
01595
01596
01597
01598
01599 name="";
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
01641
01642
01643
01644
01645 }
01646 else
01647 if(token.type_ == '<')
01648 {
01649
01650
01651
01652
01653 eat_template_parms();
01654 if(token.type_ == '>')
01655 next_token();
01656
01657 }
01658
01659
01660
01661
01662
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_ == '~')
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();
01723
01724 return false;
01725
01726 }
01727
01728 static void eat_type_name()
01729
01730
01731
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();
01765
01766 while( (!eof() && token.type_ == '*')
01767 || (token.type_ == '&' )
01768 || (token.type_ == CPP_Token::aln && token.text_ == constKeyWord)
01769 )
01770 {
01771 next_token();
01772 }
01773
01774 }
01775
01776
01777 static bool parse_typedef()
01778 {
01779 next_token();
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
01798
01799
01800
01801
01802
01803
01804 {
01805
01806 next_token();
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
01820
01821 next_token();
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
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();
01846
01847 }
01848
01849 parse_variables_defined();
01850
01851 return false;
01852 }
01853 static bool parse_namespace()
01854
01855
01856
01857 {
01858 next_token();
01859
01860 ScopeBinder old_scope(outer_scope);
01861
01862 if(token.type_ == CPP_Token::aln)
01863 {
01864
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
01899
01900
01901 {
01902 next_token();
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();
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
01941
01942
01943
01944
01945
01946
01947
01948
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
01973
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
01996
01997 }
01998
01999 static void eat_till_semicolon()
02000
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_;
02032 FunctionScope::paths_t basePaths_;
02033
02034 };
02035
02036
02037 static bool process_statement(ElseifCounterInfo *);
02038
02039 static void eat_case_blocks()
02040
02041
02042
02043
02044
02045
02046
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
02060
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
02089
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);
02103
02104 }
02105
02106
02107
02108 }
02109 else
02110 process_statement(0);
02111
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
02126
02127
02128
02129
02130
02131
02132 {
02133
02134
02135
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
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
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
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
02217
02218 if(!oneSideReturned)
02219 outer_scope.doublePaths();
02220 }
02221 else
02222 if(elseifCounter->count_)
02223 {
02224
02225
02226 outer_scope.setPaths( (1 + elseifCounter->count_) * outer_scope.paths() );
02227
02228 elseifCounter->count_ = 0;
02229 }
02230
02231 }
02232 else
02233 if(token.text_ == elseKeyWord)
02234 {
02235
02236
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();
02259
02260 eat_parenthesis();
02261
02262 if(token.type_ == ')' )
02263 {
02264
02265 next_token();
02266
02267 }
02268
02269 process_statement(0);
02270
02271
02272 }
02273 else
02274 if( token.text_ == doKeyWord)
02275 {
02276 next_token();
02277
02278 process_statement(0);
02279
02280 if( token.type_ == CPP_Token::aln
02281 && token.text_ == whileKeyWord
02282 )
02283 {
02284 outer_scope.addStatement();
02285
02286
02287
02288 next_token();
02289
02290 eat_parenthesis();
02291
02292 if( token.type_ == ')')
02293 next_token();
02294
02295 outer_scope.addStatement();
02296 eat_till_semicolon();
02297 outer_scope.doublePaths();
02298
02299 }
02300 }
02301 else
02302 if( token.text_ == switchKeyWord )
02303 {
02304 next_token();
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();
02334
02335 eat_case_blocks();
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();
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();
02388
02389
02390 next_token();
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);
02411
02412
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();
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);
02443
02444 while( token.type_ == CPP_Token::aln
02445 && token.text_ == catchKeyWord
02446 )
02447 {
02448 process_statement(0);
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
02472
02473
02474
02475
02476 eat_till_semicolon();
02477 }
02478
02479 }
02480 else
02481 {
02482 outer_scope.addStatement();
02483 eat_till_semicolon();
02484 }
02485
02486 return returnWasLastStatement;
02487 }
02488
02489
02490 static void eat_function_body()
02491
02492
02493
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();
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();
02545
02546 process_statement(0);
02547
02548 }
02549
02550 }
02551
02552
02553
02554
02555 static void eat_function_parms(string *parmtext)
02556
02557
02558
02559
02560
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
02590
02591
02592
02593
02594
02595
02596 {
02597
02598 int depth=0;
02599
02600 if(token.type_ == CPP_Token::aln &&
02601 token.text_ == classKeyWord
02602 )
02603 {
02604 next_token();
02605 next_token();
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
02642
02643
02644
02645
02646
02647
02648
02649
02650
02651
02652
02653
02654
02655
02656
02657
02658
02659
02660
02661 {
02662
02663
02664
02665
02666 CPP_Token tmp;
02667
02668 if(token.text_ == staticKeyWord)
02669 next_token();
02670
02671 do
02672 {
02673
02674
02675
02676 if(token.type_ == '{')
02677 {
02678
02679
02680 eat_curly_block();
02681
02682 if(token.type_ == '}')
02683 next_token();
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
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;
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
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
02839
02840 if( token.type_ == '=')
02841 {
02842
02843
02844
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
02867
02868
02869
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
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
02940
02941 if(token.text_ == "return")
02942 {
02943 while(!eof() && token.type_ != '{')
02944 next_token();
02945 }
02946
02947
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
02973
02974
02975
02976
02977
02978
02979
02980
02981
02982
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
03015
03016
03017
03018
03019
03020
03021 {
03022 next_token();
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
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 }