#include #include #include "nsString.h" #include "IDirectory.h" #include "CScanner.h" #include "CCPPTokenizer.h" #include "tokens.h" #include "CToken.h" #include //******************************************************************************* // Global variables... //******************************************************************************* bool gVerbose=false; bool gShowIncludeErrors=false; bool gEmitHTML=false; char gRootDir[1024]; class IPattern { public: virtual CToken* scan(CCPPTokenizer& aTokenizer,int anIndex,ostream& anOutput)=0; }; class CPattern { public: CPattern(const nsCString& aFilename) : mNames(0), mFilename(aFilename), mSafeNames(0) { mIndex=0; mLineNumber=1; mErrCount=0; } CToken* matchID(nsDeque& aDeque,nsCString& aName){ int theMax=aDeque.GetSize(); for(int theIndex=0;theIndexgetStringValue(); if(theName==aName) { return theToken; } } } return 0; } /*************************************************************************** This gets called when we know we've seen an if statement (at anIndex). Look into the parm list (...) for an ID. If you find one, then you know it's a conditioned identifier which can be safely ignored during other error tests. ***************************************************************************/ CToken* findSafeIDInIfStatement(CCPPTokenizer& aTokenizer,nsDeque& aNameDeque) { CToken* result=0; enum eState {eSkipLparen,eFindID}; eState theState=eSkipLparen; CToken* theToken=0; while(theToken=aTokenizer.getTokenAt(++mIndex)) { int theType=theToken->getTokenType(); nsCString& theString=theToken->getStringValue(); PRUnichar theChar=theString[0]; switch(theState) { case eSkipLparen: //first scan for the lparen... switch(theType) { case eToken_comment: case eToken_whitespace: case eToken_newline: mLineNumber+=theString.CountChar('\n'); break; case eToken_operator: switch(theChar) { case '(': theState=eFindID; break; case ')': return 0; break; default: break; } //switch break; default: break; } //switch break; //now scan for the identifier... case eFindID: switch(theType) { case eToken_comment: case eToken_whitespace: case eToken_newline: mLineNumber+=theString.CountChar('\n'); break; case eToken_identifier: if(matchID(mNames,theString)) { return theToken; } break; case eToken_operator: switch(theChar) { case ')': case ';': case '{': case '(': return result; break; default: break; } //switch break; default: break; } //switch break; default: switch(theType) { case eToken_comment: case eToken_whitespace: case eToken_newline: mLineNumber+=theString.CountChar('\n'); break; default: break; }//switch break; } //switch } return result; } /*************************************************************************** We're looking for the pattern foo-> without the preceeding (conditional) if(): Assume that the given id (aName) exists in the tokenizer at anIndex. if(foo) foo-> ***************************************************************************/ CToken* findPtrDecl(CCPPTokenizer& aTokenizer,int anIndex,PRUnichar& anOpChar,bool allocRequired) { enum eState {eFindNew,eFindType,eFindPunct,eFindStar}; CToken* theToken=0; CToken* theID=0; eState theState=eFindStar; while(theToken=aTokenizer.getTokenAt(++anIndex)){ if(theToken) { nsCString& theString=theToken->getStringValue(); int theType=theToken->getTokenType(); anOpChar=theString[0]; switch(theType){ case eToken_semicolon: case eToken_operator: switch(theState) { case eFindStar: if('*'==anOpChar) theState=eFindType; else return 0; break; case eFindPunct: switch(anOpChar) { case '=': if(!allocRequired) return theID; else theState=eFindNew; break; case ';': return (allocRequired) ? 0 : theID; break; default: break; } break; default: return 0; } break; case eToken_identifier: switch(theState){ case eFindType: theID=theToken; theState=eFindPunct; break; case eFindNew: if(theString.Equals("new") || theString.Equals("PR_Malloc")) return theID; else return 0; break; default: return 0; } break; default: break; } }//if }//while return 0; } /*************************************************************************** We're looking for the pattern *foo. ***************************************************************************/ bool hasSimpleDeref(nsCString& aName,CCPPTokenizer& aTokenizer,int anIndex) { bool result=false; int theMin=anIndex-3; for(int theIndex=anIndex-1;theIndex>theMin;theIndex--){ CToken* theToken=aTokenizer.getTokenAt(theIndex); if(theToken) { nsCString& theString=theToken->getStringValue(); int theType=theToken->getTokenType(); PRUnichar theChar=theString[0]; if(eToken_operator==theType) { if('*'==theChar){ result=true; } } } } if(result) { int theMax=anIndex+3; for(int theIndex=anIndex;theIndexgetStringValue(); int theType=theToken->getTokenType(); PRUnichar theChar=theString[0]; if(eToken_operator==theType) { if((','==theChar) || (')'==theChar)){ result=false; } } } } } return result; } /*************************************************************************** We're looking for the pattern foo-> without the preceeding (conditional) if(): Assume that the given id (aName) exists in the tokenizer at anIndex. if(foo) foo-> ***************************************************************************/ bool hasUnconditionedDeref(nsCString& aName,CCPPTokenizer& aTokenizer,int anIndex) { bool result=false; enum eState {eFailure,eFindIf,eFindDeref}; CToken* theToken=0; eState theState=eFindDeref; int theIndex=anIndex+1; while(eFailure!=theState){ CToken* theToken=aTokenizer.getTokenAt(theIndex); if(theToken) { nsCString& theString=theToken->getStringValue(); int theType=theToken->getTokenType(); PRUnichar theChar=theString[0]; switch(theState){ case eFindDeref: switch(theType) { case eToken_whitespace: case eToken_comment: case eToken_newline: theIndex++; break; default: if(0==theString.Compare("->",true,2)) { theIndex=anIndex-1; //now start looking backwards... theState=eFindIf; } else theState=eFailure; }//switch break; case eFindIf: switch(theType) { case eToken_whitespace: case eToken_comment: case eToken_newline: theIndex--; break; case eToken_semicolon: return true; break; case eToken_operator: switch(theChar){ case '{': case '+': case '-': case '=': case '*': case '%': return true; default: break; } theIndex-=1; break; case eToken_identifier: if(0==theString.Compare("if",true,2)){ return false; } else if(0==theString.Compare(aName)){ return false; } else if(0==theString.Compare("while",true,5)){ return false; } return true; default: theIndex--; } //switch break; }//switch }//if else theState=eFailure; }//while return result; } void skipSemi(CCPPTokenizer& aTokenizer) { CToken* theToken=0; while(theToken=aTokenizer.getTokenAt(++mIndex)){ int theType=theToken->getTokenType(); nsCString& theString=theToken->getStringValue(); PRUnichar theChar=theString[0]; switch(theType){ case eToken_semicolon: return; case eToken_operator: if(('}'==theChar) || (';'==theChar)) return; break; case eToken_newline: case eToken_comment: case eToken_whitespace: mLineNumber+=theString.CountChar('\n'); default: break; } //switch }//while } void OutputMozillaFilename(ostream& anOutput) { PRInt32 pos=mFilename.Find("mozilla"); const char* buf=mFilename.GetBuffer(); if(-1"; } cout << buf << ":" << mLineNumber; if(gEmitHTML) { cout << ""; } } else { anOutput << buf; } } /*************************************************************************** This method iterates the tokens (in tokenizer) looking for statements. The statements are then decoded to find pointer variables. If they get a new call, then they're recorded in mNames. If they're in an IF(), then they're recording in mSafeNames. We also look for simple dereferences. When we find one, we look it up in mSafeNames. If it's not there, we have a potential deref error. ***************************************************************************/ void scan(CCPPTokenizer& aTokenizer,ostream& anOutput){ int theMax=aTokenizer.getCount(); int theIDCount=mNames.GetSize(); int theSafeIDCount=mSafeNames.GetSize(); CToken* theMatch=0; while(mIndexgetTokenType(); nsCString& theString=theToken->getStringValue(); int theNLCount=theString.CountChar('\n'); switch(theType){ case eToken_operator: { PRUnichar theChar=theString[0]; switch(theChar){ case '{': mIndex++; scan(aTokenizer,anOutput); break; case '}': //before returning, let's remove the new identifiers... while(mNames.GetSize()>theIDCount){ CToken* theToken2=(CToken*)mNames.Pop(); } while(mSafeNames.GetSize()>theSafeIDCount){ CToken* theToken2=(CToken*)mSafeNames.Pop(); } return; default: break; } } break; case eToken_comment: case eToken_newline: case eToken_whitespace: mLineNumber+=theString.CountChar('\n'); break; //If it's an ID, then we're looking for 3 patterns: // 1. ID* ID...; // 2. *ID=ID; // 3. ID->XXX; case eToken_identifier: { PRUnichar theOpChar=0; if(theToken=findPtrDecl(aTokenizer,mIndex,theOpChar,true)) { //we found ID* ID; (or) ID* ID=...; mNames.Push(theToken); mIndex+=2; skipSemi(aTokenizer); } else if(0==theString.Compare("if",true,2)) { CToken* theSafeID=findSafeIDInIfStatement(aTokenizer,mNames); if(theSafeID) { mSafeNames.Push(theSafeID); theSafeIDCount++; } } else { CToken* theMatch=matchID(mNames,theString); if(theMatch){ if((hasUnconditionedDeref(theString,aTokenizer,mIndex)) || (hasSimpleDeref(theString,aTokenizer,mIndex))){ CToken* theSafeID=matchID(mSafeNames,theString); if(theSafeID) { nsCString& s1=theSafeID->getStringValue(); if(s1.Equals(theString)) break; } //dump the name out in LXR format OutputMozillaFilename(anOutput); nsCAutoString theCStr(theString); anOutput << " Deref-error: \"" << (const char*)theCStr << "\"" << endl; mErrCount++; } } } } break; default: break; } mIndex++; } } nsDeque mNames; nsDeque mSafeNames; int mIndex; int mLineNumber; int mErrCount; nsCString mFilename; }; void ScanFile(nsCString& aFilename,ostream& anOutput,int& aLineCount,int& anErrCount) { nsCAutoString theCStr(aFilename); fstream input((const char*)theCStr,ios::in); CScanner theScanner(input); CCPPTokenizer theTokenizer; theTokenizer.tokenize(theScanner); CPattern thePattern(aFilename); thePattern.scan(theTokenizer,anOutput); aLineCount+=thePattern.mLineNumber; anErrCount+=thePattern.mErrCount; } void IterateFiles(IDirectory& aDirectory,ostream& anOutput,int& aFilecount,int& aLineCount,int& anErrCount) { nsCAutoString thePath(""); aDirectory.getPath(thePath); int theCount=aDirectory.getSize(); int theIndex=0; for(theIndex=0;theIndexgetName(); if(!dynamic_cast(theEntry)){ nsCAutoString theCopy; theName.Right(theCopy,4); if(0==theCopy.Compare(".cpp",true,3)) { nsCAutoString path(thePath); path.Append('/'); path.Append(theName); aFilecount++; ScanFile(path,anOutput,aLineCount,anErrCount); } } else { //we've got a directory on our hands... //let's go walk it... nsCAutoString path(thePath); path.Append("/"); path.Append(theName); if(!theName.EqualsIgnoreCase("cvs")) { IDirectory* theDirectory=IDirectory::openDirectory(path.GetBuffer(),"*.*"); if(theDirectory) { IterateFiles(*theDirectory,anOutput,aFilecount,aLineCount,anErrCount); IDirectory::closeDirectory(theDirectory); } } } } } void ConsumeArguments(int argc, char* argv[]) { if(argc<2) { cout << "Usage: dreftool -h -d sourcepath" << endl; cout << "-D path to root of source tree" << endl; cout << "-H emit as HTML" << endl; exit(1); } for(int index=1;index" << endl; } cout << "Scanning " << gRootDir << "..." << endl; IDirectory* theDirectory=IDirectory::openDirectory(gRootDir,"*.*"); if(theDirectory) IterateFiles(*theDirectory,cout,fileCount,lineCount,errCount); #endif if(gEmitHTML) { cout << "
" <" <" << endl;
  }


  return 0;
}