Index: test/modularize/Inputs/InconsistentSubHeader.h =================================================================== --- test/modularize/Inputs/InconsistentSubHeader.h (revision 183718) +++ test/modularize/Inputs/InconsistentSubHeader.h (working copy) @@ -1,8 +1,8 @@ // Set up so TypeInt only defined during InconsistentHeader1.h include. -#ifdef SYMBOL1 +#if defined(SYMBOL1) && !defined(SYMBOL) #define SYMBOL 1 #endif -#ifdef SYMBOL2 +#if defined(SYMBOL2) && !defined(SYMBOL) #define SYMBOL 2 #endif Index: test/modularize/ProblemsInconsistent.modularize =================================================================== --- test/modularize/ProblemsInconsistent.modularize (revision 183718) +++ test/modularize/ProblemsInconsistent.modularize (working copy) @@ -6,7 +6,16 @@ # CHECK: error: macro 'SYMBOL' defined at multiple locations: # CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9 # CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:6:9 -# CHECK-NEXT: error: header '{{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h' has different contents dependening on how it was included +# CHECK-NEXT:warning: The instances of header {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h have different contents after preprocessing: +# CHECK-NEXT: When included or nested in these top-level headers: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h +# CHECK-NEXT: This preprocessor directive is mismatched (the parentheses show the condition after macro substitution): +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:9:1: #if SYMBOL == 1 (#if 1 == 1) +# CHECK-NEXT: When included or nested in these top-level headers: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h +# CHECK-NEXT: This preprocessor directive is mismatched (the parentheses show the condition after macro substitution): +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:9:1: #if SYMBOL == 1 (#if 2 == 1) +# CHECK-NEXT: error: header '{{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h' has different contents depending on how it was included # CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 3:9 not always provided # CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 6:9 not always provided # CHECK-NEXT: note: 'TypeInt' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 10:13 not always provided Index: modularize/ModularizeHeaderTracker.cpp =================================================================== --- modularize/ModularizeHeaderTracker.cpp (revision 0) +++ modularize/ModularizeHeaderTracker.cpp (revision 0) @@ -0,0 +1,134 @@ +//==- ModularizeHeaderTracker.cpp - Represents header instances -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ModularizeHeaderTracker.h" +#include "ModularizeUtilities.h" +#include "llvm/Support/raw_ostream.h" + +namespace Modularize { +using namespace llvm; +using namespace clang; + +ModularizeHeaderTracker::ModularizeHeaderTracker( + std::string& name, ModularizeHeaderFile *headerFile, + std::string topHeader) : + Name(name) { + ModularizeHeaderInstance *headerInstance = new ModularizeHeaderInstance( + headerFile, topHeader); + addHeaderInstance(headerInstance); +} + +ModularizeHeaderTracker::~ModularizeHeaderTracker() { +} + +// Process a header snapshot. +// If the snapshot differs, add a new header instance. +void ModularizeHeaderTracker::addHeaderFile( + ModularizeHeaderFile *headerFile, std::string topHeader) { + ModularizeHeaderInstanceVectorIterator iter = HeaderInstances.begin(); + ModularizeHeaderInstanceVectorIterator EI = HeaderInstances.end(); + for (; iter != EI; ++iter) { + ModularizeHeaderFile *existingHeader = (*iter)->getHeaderFile(); + if (existingHeader->match(headerFile)) { + (*iter)->addTopHeader(topHeader); + return; + } + } + ModularizeHeaderInstance *newInstance = new ModularizeHeaderInstance( + headerFile, topHeader); + addHeaderInstance(newInstance); +} + +// Add a header instance. +void ModularizeHeaderTracker::addHeaderInstance( + ModularizeHeaderInstance *headerInstance) { + HeaderInstances.push_back(headerInstance); +} + +// Report instances where header snapshots differ. +// Returns true if no errors, i.e. no differing snapshots. +bool ModularizeHeaderTracker::report() { + int headerInstanceIndex; + int headerInstanceCount = getHeaderInstanceCount(); + ModularizeHeaderInstance *headerInstance; + ModularizeHeaderInstance *headerInstance0; + ModularizeHeaderFile *headerFile; + ModularizeHeaderFile *headerFile0; + int directiveIndex; + ModularizePPDirective *directive; + ModularizePPDirective *directive0; + bool mismatch; + if (getHeaderInstanceCount() > 1) { + int directiveCount = 0x7fffffff; // Big number. + ModularizeHeaderInstanceVectorIterator hiIter = getHeaderInstancesBegin(); + ModularizeHeaderInstanceVectorIterator hiEnd = getHeaderInstancesEnd(); + // Get the number of instances which is least. + for (; hiIter != hiEnd; ++hiIter) { + headerInstance = (*hiIter); + headerFile = headerInstance->getHeaderFile(); + if (headerFile->getDirectiveCount() < directiveCount) + directiveCount = headerFile->getDirectiveCount(); + } + for (directiveIndex = 0; directiveIndex < directiveCount; + directiveIndex++) { + headerInstance0 = getHeaderInstance(0); + headerFile0 = headerInstance0->getHeaderFile(); + directive0 = &headerFile0->getDirective(directiveIndex); + mismatch = false; + for (headerInstanceIndex = 1; headerInstanceIndex < headerInstanceCount; + headerInstanceIndex++) { + headerInstance = getHeaderInstance(headerInstanceIndex); + headerFile = headerInstance->getHeaderFile(); + directive = &headerFile->getDirective(directiveIndex); + if (!directive->match(directive0)) + mismatch = true; + } + if (mismatch) { + errs() << "warning: The instances of header " << Name << " have different contents after preprocessing:\n"; + for (headerInstanceIndex = 0; headerInstanceIndex < headerInstanceCount; + headerInstanceIndex++) { + headerInstance = getHeaderInstance(headerInstanceIndex); + headerFile = headerInstance->getHeaderFile(); + errs() << getIndent(1) << "When included or nested in these top-level headers:\n"; + ModularizeTopHeaderVectorIterator thIter = headerInstance->getTopHeadersBegin(); + ModularizeTopHeaderVectorIterator thEnd = headerInstance->getTopHeadersEnd(); + for (; thIter != thEnd; ++thIter) { + errs() << getIndent(2) << *thIter << "\n"; + } + errs() << getIndent(1) << "This preprocessor directive is mismatched (the parentheses show the condition after macro substitution):\n"; + directive = &headerFile->getDirective(directiveIndex); + directive->dump(2, Name.c_str()); + } + break; // Stop here, because the blocks might be different after this. + } + } + return false; // There was a mismatch. + } + return true; // No mismatch. +} + +// Display the information for debugging. +void ModularizeHeaderTracker::dump(int level) { + errs() << getIndent(level) << Name << ":\n"; + errs() << getIndent(level + 1) << "instance count: " + << getHeaderInstanceCount() << "\n"; + + errs() << getIndent(level + 1) << "instances: \n"; + ModularizeHeaderInstanceVectorIterator hiIter = getHeaderInstancesBegin(); + ModularizeHeaderInstanceVectorIterator hiEnd = getHeaderInstancesEnd(); + int hiIndex; + for (hiIndex = 0; hiIter != hiEnd; ++hiIter, ++hiIndex) { + ModularizeHeaderInstance *headerInstance = (*hiIter); + errs() << getIndent(level + 2) << "instance: " << hiIndex << "\n"; + headerInstance->dump(level + 3); + } +} + +} + Index: modularize/ModularizeHeaderTracker.h =================================================================== --- modularize/ModularizeHeaderTracker.h (revision 0) +++ modularize/ModularizeHeaderTracker.h (revision 0) @@ -0,0 +1,67 @@ +//===- ModularizeHeaderTracker.h - Represents header instances -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines a class to store header instance snapshot information +/// for one header file. +/// +//===----------------------------------------------------------------------===// + +#ifndef MODULARIZEHEADERTRACKER_H +#define MODULARIZEHEADERTRACKER_H + +#include "ModularizeHeaderInstance.h" + +namespace Modularize { + using namespace clang; + using namespace llvm; + +/// \brief This class stores the header snapshot instances for a particular +/// header. +class ModularizeHeaderTracker { +public: + ModularizeHeaderTracker(std::string& name, ModularizeHeaderFile *headerFile, + std::string topHeader); + ~ModularizeHeaderTracker(); + + // Header name accessors. + std::string& getName() { return Name; } + void setName(std::string name) { Name = name; }; + + // Process a header snapshot. + // If the snapshot differs, add a new header instance. + void addHeaderFile(ModularizeHeaderFile *headerFile, std::string topHeader); + // Add a header instance. + void addHeaderInstance(ModularizeHeaderInstance *headerInstance); + // Get instance count. > 1 means there were mismatches. + int getHeaderInstanceCount() { return HeaderInstances.size(); } + // Get header instance vector. + ModularizeHeaderInstanceVector &getHeaderInstances() { return HeaderInstances; } + ModularizeHeaderInstanceVectorIterator getHeaderInstancesBegin() { return HeaderInstances.begin(); } + ModularizeHeaderInstanceVectorIterator getHeaderInstancesEnd() { return HeaderInstances.end(); } + ModularizeHeaderInstance *getHeaderInstance(int index) { return HeaderInstances[index]; } + + // Report instances where header snapshots differ. + // Returns true if no errors, i.e. no differing snapshots. + bool report(); + // Display the information for debugging. + void dump(int level = 0); + +private: + std::string Name; + ModularizeHeaderInstanceVector HeaderInstances; +}; + +// Collections for users of this typs. +typedef StringMap ModularizeHeaderTrackerMap; +typedef ModularizeHeaderTrackerMap::iterator ModularizeHeaderTrackerMapIterator; + +} // end namespace clang + +#endif Index: modularize/ModularizeHeaderInstance.cpp =================================================================== --- modularize/ModularizeHeaderInstance.cpp (revision 0) +++ modularize/ModularizeHeaderInstance.cpp (revision 0) @@ -0,0 +1,56 @@ +//==- ModularizeHeaderInstance.cpp - Stores header instance info -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ModularizeHeaderInstance.h" +#include "ModularizeUtilities.h" +#include "llvm/Support/raw_ostream.h" +#include "clang/Basic/SourceManager.h" + +namespace Modularize { +using namespace llvm; +using namespace clang; + +ModularizeHeaderInstance::ModularizeHeaderInstance( + ModularizeHeaderFile *HeaderFile, std::string topHeader) : + HeaderFile(HeaderFile) { + TopHeaders.push_back(topHeader); +} + +ModularizeHeaderInstance::~ModularizeHeaderInstance() { +} + +bool ModularizeHeaderInstance::hasTopHeader(std::string topHeader) { + ModularizeTopHeaderVectorIterator iter = TopHeaders.begin(); + ModularizeTopHeaderVectorIterator end = TopHeaders.begin(); + for (; iter != end; ++iter) { + if (*iter == topHeader) + return true; + } + return false; +} + +// Display the information for debugging. +void ModularizeHeaderInstance::dump(int level) { + errs() << getIndent(level) << "top headers:\n"; + + ModularizeTopHeaderVectorIterator thIter = getTopHeadersBegin(); + ModularizeTopHeaderVectorIterator thEnd = getTopHeadersEnd(); + for (; thIter != thEnd; ++thIter) { + errs() << getIndent(level + 1) << *thIter << "\n"; + } + + errs() << getIndent(level) << "preprocessor directives: \n"; + ModularizePPDirectiveVectorIterator dIter = HeaderFile->getDirectivesBegin(); + ModularizePPDirectiveVectorIterator dEnd = HeaderFile->getDirectivesEnd(); + for (; dIter != dEnd; ++dIter) { + (*dIter).dump(level + 1, HeaderFile->getName().c_str()); + } +} + +} Index: modularize/ModularizePPDirective.cpp =================================================================== --- modularize/ModularizePPDirective.cpp (revision 0) +++ modularize/ModularizePPDirective.cpp (revision 0) @@ -0,0 +1,96 @@ +//===--- ModularizePPDirective.cpp - Represents a PP directive --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ModularizePPDirective.h" +#include "ModularizeUtilities.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/raw_ostream.h" +#include + +#if _MSC_VER >= 1400 // VC 8.0 and later deprecate snprintf and _snprintf. +# define snprintf _snprintf_s +#elif _MSC_VER +# define snprintf _snprintf +#endif + +namespace Modularize { +using namespace llvm; +using namespace clang; + +ModularizePPDirective::ModularizePPDirective( + ModularizePPDirectiveKind directiveKind, unsigned lineNumber, + std::string unconvertedConditional, std::string convertedConditional) : + DirectiveKind(directiveKind), + LineNumber(lineNumber), + UnconvertedConditional(unconvertedConditional), + ConvertedConditional(convertedConditional) { +} + +ModularizePPDirective::~ModularizePPDirective() { +} + +// Return true if this directive matches another. +// (It compares the converted conditionals.) +bool ModularizePPDirective::match(ModularizePPDirective *other) { + if (ConvertedConditional == other->getConvertedConditional()) { + return true; + } + return false; +} + +// Print this directive to a string suitable for a message. +// FIXME: The following was to avoid the apparent portability +// problems with snprintf. Perhaps there was a better way +// with just std::string or other string utilities I don't +// know about, but the following has fewer allocations. +std::string ModularizePPDirective::printToString( + int level, std::string fileName) { + char buffer[1024]; + char lineBuffer[80]; + snprintf(lineBuffer, sizeof(lineBuffer) - 1, ":%d:1", LineNumber); + strncpy(buffer, getIndent(level).c_str(), sizeof(buffer) - 1); + strncat(buffer, fileName.c_str(), sizeof(buffer) - 1); + strncat(buffer, lineBuffer, sizeof(buffer) - 1); + strncat(buffer, ": ", sizeof(buffer) - 1); + const char *directiveString; + switch (DirectiveKind) { + case MPPD_If: + directiveString = "#if "; + break; + case MPPD_ElIf: + directiveString = "#elif "; + break; + case MPPD_IfDef: + directiveString = "#ifdef "; + break; + case MPPD_IfNDef: + directiveString = "#ifndef "; + break; + default: + directiveString = "#unknown "; + break; + } + strncat(buffer, directiveString, sizeof(buffer) - 1); + strncat(buffer, UnconvertedConditional.c_str(), sizeof(buffer) - 1); + strncat(buffer, " (", sizeof(buffer) - 1); + strncat(buffer, directiveString, sizeof(buffer) - 1); + strncat(buffer, ConvertedConditional.c_str(), sizeof(buffer) - 1); + strncat(buffer, ")\n", sizeof(buffer) - 1); + buffer[sizeof(buffer) - 1] = '\0'; + return std::string(buffer); +} + +// Display the information for debugging. +void ModularizePPDirective::dump(int level, const char *fileName) { + if (fileName == NULL) + fileName = "line"; + errs() << printToString(level, fileName); +} + +} Index: modularize/Modularize.cpp =================================================================== --- modularize/Modularize.cpp (revision 183718) +++ modularize/Modularize.cpp (working copy) @@ -39,7 +39,23 @@ // (file):(row):(column) // (file):(row):(column) // -// error: header '(file)' has different contents dependening on how it was +// The following message might appear if preprocessor conditional directives +// resolve differently. +// +// warning: The instances of header (file) have different contents after +// preprocessing: +// When included or nested in these top-level headers: +// (file list) +// This preprocessor directive is mismatched (the parentheses show the +// condition after macro substitution): +// (file):(line):1: #if SYMBOL == 1 (#if 1 == 1) +// When included or nested in these top-level headers: +// (file list) +// This preprocessor directive is mismatched (the parentheses show the +// condition after macro substitution): +// (file):(line):1: #if SYMBOL == 1 (#if 2 == 1) +// +// error: header '(file)' has different contents depending on how it was // included // // The latter might be followed by messages like the following: @@ -53,20 +69,20 @@ // // Some ideas: // -// 1. Try to figure out the preprocessor conditional directives that -// contribute to problems. -// -// 2. Check for correct and consistent usage of extern "C" {} and other +// 1. Check for correct and consistent usage of extern "C" {} and other // directives. Warn about #include inside extern "C" {}. // -// 3. What else? +// 2. What else? // -// General clean-up and refactoring: +// General clean-up, refactoring, and fixing: // // 1. The Location class seems to be something that we might // want to design to be applicable to a wider range of tools, and stick it // somewhere into Tooling/ in mainline // +// 2. The header preprocessing instance checking has some limitations that +// need to be address. It currently can only handle non-function-like macros. +// //===----------------------------------------------------------------------===// #include "clang/AST/ASTConsumer.h" @@ -90,34 +106,47 @@ #include #include #include +#include "ModularizeMasterHeaderTracker.h" +#include "ModularizePPCallbacks.h" using namespace clang::tooling; using namespace clang; using namespace llvm; +using namespace Modularize; +// Option to specify a prefix to be prepended to the header names. +cl::opt HeaderPrefix( + "prefix", cl::init(""), + cl::desc( + "Prepend header file paths with this prefix." + " If not specified," + " the files are considered to be relative to the header list file.")); + +// Fixme: This option doesn't seem to work. +// Option to disable the header preprocessing check. +cl::opt NoPreprocessingCheck( + "no-preprocessing-check", + cl::desc( + "If this option is specified, no header preprocessing check" + " will be performed. This is a fallback in case there are problems" + " with the checking itself."), + cl::init(false)); + // Option to specify a file name for a list of header files to check. cl::opt -ListFileName(cl::Positional, +ListgetFileName(cl::Positional, cl::desc("")); // Collect all other arguments, which will be passed to the front end. cl::list CC1Arguments( cl::ConsumeAfter, cl::desc("...")); -// Option to specify a prefix to be prepended to the header names. -cl::opt HeaderPrefix( - "prefix", cl::init(""), - cl::desc( - "Prepend header file paths with this prefix." - " If not specified," - " the files are considered to be relative to the header list file.")); - // Read the header list file and collect the header file names. -error_code GetHeaderFileNames(SmallVectorImpl &headerFileNames, - StringRef listFileName, StringRef headerPrefix) { +error_code getHeadergetFileNames(SmallVectorImpl &headergetFileNames, + StringRef listgetFileName, StringRef headerPrefix) { // By default, use the path component of the list file name. - SmallString<256> headerDirectory(listFileName); + SmallString<256> headerDirectory(listgetFileName); sys::path::remove_filename(headerDirectory); // Get the prefix if we have one. @@ -126,7 +155,7 @@ // Read the header list file into a buffer. OwningPtr listBuffer; - if (error_code ec = MemoryBuffer::getFile(ListFileName, listBuffer)) { + if (error_code ec = MemoryBuffer::getFile(ListgetFileName, listBuffer)) { return ec; } @@ -142,16 +171,16 @@ // Ignore comments and empty lines. if (line.empty() || (line[0] == '#')) continue; - SmallString<256> headerFileName; + SmallString<256> headergetFileName; // Prepend header file name prefix if it's not absolute. if (sys::path::is_absolute(line)) - headerFileName = line; + headergetFileName = line; else { - headerFileName = headerDirectory; - sys::path::append(headerFileName, line); + headergetFileName = headerDirectory; + sys::path::append(headergetFileName, line); } // Save the resulting header file path. - headerFileNames.push_back(headerFileName.str()); + headergetFileNames.push_back(headergetFileName.str()); } return error_code::success(); @@ -380,8 +409,14 @@ class CollectEntitiesConsumer : public ASTConsumer { public: - CollectEntitiesConsumer(EntityMap &Entities, Preprocessor &PP) - : Entities(Entities), PP(PP) {} + CollectEntitiesConsumer( + EntityMap &Entities, + ModularizeMasterHeaderTracker &masterHeaderTracker, + Preprocessor &PP, StringRef InFile) + : Entities(Entities), MasterHeaderTracker(masterHeaderTracker), PP(PP) { + if (!masterHeaderTracker.isDisabled()) + PP.addPPCallbacks(new ModularizePPCallbacks(PP, InFile, masterHeaderTracker)); + } virtual void HandleTranslationUnit(ASTContext &Ctx) { SourceManager &SM = Ctx.getSourceManager(); @@ -406,30 +441,39 @@ } private: EntityMap &Entities; + ModularizeMasterHeaderTracker &MasterHeaderTracker; Preprocessor &PP; }; class CollectEntitiesAction : public SyntaxOnlyAction { public: - CollectEntitiesAction(EntityMap &Entities) : Entities(Entities) {} + CollectEntitiesAction( + EntityMap &Entities, ModularizeMasterHeaderTracker &masterHeaderTracker) + : Entities(Entities), MasterHeaderTracker(masterHeaderTracker) {} protected: virtual clang::ASTConsumer * CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - return new CollectEntitiesConsumer(Entities, CI.getPreprocessor()); + return new CollectEntitiesConsumer( + Entities, MasterHeaderTracker, CI.getPreprocessor(), InFile); } private: EntityMap &Entities; + ModularizeMasterHeaderTracker &MasterHeaderTracker; }; class ModularizeFrontendActionFactory : public FrontendActionFactory { public: - ModularizeFrontendActionFactory(EntityMap &Entities) : Entities(Entities) {} + ModularizeFrontendActionFactory( + EntityMap &Entities, + ModularizeMasterHeaderTracker &masterHeaderTracker) + : Entities(Entities), MasterHeaderTracker(masterHeaderTracker) {} virtual CollectEntitiesAction *create() { - return new CollectEntitiesAction(Entities); + return new CollectEntitiesAction(Entities, MasterHeaderTracker); } private: EntityMap &Entities; + ModularizeMasterHeaderTracker &MasterHeaderTracker; }; int main(int argc, const char **argv) { @@ -438,15 +482,15 @@ cl::ParseCommandLineOptions(argc, argv, "modularize.\n"); // No go if we have no header list file. - if (ListFileName.size() == 0) { + if (ListgetFileName.size() == 0) { cl::PrintHelpMessage(); return 1; } // Get header file names. SmallVector Headers; - if (error_code ec = GetHeaderFileNames(Headers, ListFileName, HeaderPrefix)) { - errs() << argv[0] << ": error: Unable to get header list '" << ListFileName + if (error_code ec = getHeadergetFileNames(Headers, ListgetFileName, HeaderPrefix)) { + errs() << argv[0] << ": error: Unable to get header list '" << ListgetFileName << "': " << ec.message() << '\n'; return 1; } @@ -458,10 +502,14 @@ Compilations.reset( new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments)); + // Create the master header tracker. + ModularizeMasterHeaderTracker MasterHeaderTracker(NoPreprocessingCheck); + // Parse all of the headers, detecting duplicates. EntityMap Entities; ClangTool Tool(*Compilations, Headers); - int HadErrors = Tool.run(new ModularizeFrontendActionFactory(Entities)); + int HadErrors = Tool.run( + new ModularizeFrontendActionFactory(Entities, MasterHeaderTracker)); // Create a place to save duplicate entity locations, separate bins per kind. typedef SmallVector LocationArray; @@ -509,6 +557,10 @@ } } + // Report on header preprocessing mismatches. + if (!MasterHeaderTracker.report()) + HadErrors = 1; + // Complain about any headers that have contents that differ based on how // they are included. // FIXME: Could we provide information about which preprocessor conditionals @@ -524,7 +576,7 @@ HadErrors = 1; errs() << "error: header '" << H->first->getName() - << "' has different contents dependening on how it was included\n"; + << "' has different contents depending on how it was included\n"; for (unsigned I = 0, N = H->second.size(); I != N; ++I) { errs() << "note: '" << H->second[I].Name << "' in " << H->second[I] .Loc.File->getName() << " at " << H->second[I].Loc.Line << ":" Index: modularize/ModularizePPCallbacks.cpp =================================================================== --- modularize/ModularizePPCallbacks.cpp (revision 0) +++ modularize/ModularizePPCallbacks.cpp (revision 0) @@ -0,0 +1,324 @@ +//===--- PPCallbacks.cpp - Callbacks for Preprocessor actions ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ModularizePPCallbacks.h" +#include "ModularizeUtilities.h" +#include "llvm/Support/raw_ostream.h" +#include "clang/Basic/SourceManager.h" +#include +#include +#include + +namespace Modularize { +using namespace llvm; +using namespace clang; + +ModularizePPCallbacks::ModularizePPCallbacks(Preprocessor& pp, StringRef topFile, + ModularizeMasterHeaderTracker &masterHeaderTracker) : + MasterHeaderTracker(masterHeaderTracker), + TopFile(topFile.str()), + PP(pp), + RootHeaderFile(NULL), + CurrentHeaderFile(NULL) { + ModularizeHeaderFile *mhf = new ModularizeHeaderFile(topFile.str(), PP); + addHeaderFile(mhf); + CurrentHeaderFile = mhf; + RootHeaderFile = mhf; +} + +ModularizePPCallbacks::~ModularizePPCallbacks() { +} + +void ModularizePPCallbacks::FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) { + std::string locgetFileName(getSourceLocationFile(Loc)); + ModularizeHeaderFile *mhf = NULL; + std::string *fileName = NULL; + switch (Reason) { + case EnterFile: + fileName = &locgetFileName; + break; + case ExitFile: + if (PrevFID.isInvalid()) + fileName = &TopFile; + else + fileName = &locgetFileName; + break; + case SystemHeaderPragma: + return; + case RenameFile: + return; + default: + return; + } + if (fileName == NULL) + return; + mhf = getHeaderFile(*fileName); + if (mhf == NULL) { + mhf = new ModularizeHeaderFile(*fileName, PP); + addHeaderFile(mhf); + CurrentHeaderFile = mhf; + if (!RootHeaderFile) + RootHeaderFile = mhf; + } + else + CurrentHeaderFile = mhf; +} + +void ModularizePPCallbacks::EndOfMainFile() { + // Add all the header snapshots to the master header file tracker. + ModularizeHeaderFileMapIterator iter = HeaderFileMap.begin(); + ModularizeHeaderFileMapIterator end = HeaderFileMap.end(); + for (; iter != end; ++iter) { + MasterHeaderTracker.addHeaderFile((*iter).second, TopFile); + } +} + +void ModularizePPCallbacks::MacroExpands(const Token &MacroNameTok, const MacroDirective *MD, + SourceRange Range, const MacroArgs *Args) { + std::string macroName = MacroNameTok.getIdentifierInfo()->getName().str(); + std::string value; + const MacroInfo *MI = PP.getMacroInfo(MacroNameTok.getIdentifierInfo()); + MacroInfo::tokens_iterator I = MI->tokens_begin(); + MacroInfo::tokens_iterator E = MI->tokens_end(); + for (; I != E; ++I) + value += PP.getSpelling((*I)); + addSymbol(macroName, value); +} + +void ModularizePPCallbacks::If(SourceLocation Loc, SourceRange ConditionRange) { + std::string unconvertedConditional(getSourceSnippet(ConditionRange)); + std::string convertedConditional(getMacroSubstitutedString(unconvertedConditional)); + unsigned lineNumber = getSourceLocationLineNumber(Loc); + if (CurrentHeaderFile->getLastLineNumber() < lineNumber) { + ModularizePPDirective directive( + MPPD_If, lineNumber, + unconvertedConditional, convertedConditional); + CurrentHeaderFile->addDirective(directive); + } +} + +void ModularizePPCallbacks::Elif(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc) { + std::string unconvertedConditional(getSourceSnippet(ConditionRange)); + std::string convertedConditional(getMacroSubstitutedString(unconvertedConditional)); + unsigned lineNumber = getSourceLocationLineNumber(Loc); + if (CurrentHeaderFile->getLastLineNumber() < lineNumber) { + ModularizePPDirective directive( + MPPD_ElIf, getSourceLocationLineNumber(Loc), + unconvertedConditional, convertedConditional); + CurrentHeaderFile->addDirective(directive); + } +} + +void ModularizePPCallbacks::Ifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDirective *MD) { + std::string macroName = MacroNameTok.getIdentifierInfo()->getName().str(); + std::string unconvertedConditional(macroName); + std::string convertedConditional( + macroName + (MD ? " (defined)" : " (not defined)")); + unsigned lineNumber = getSourceLocationLineNumber(Loc); + if (CurrentHeaderFile->getLastLineNumber() < lineNumber) { + ModularizePPDirective directive( + MPPD_IfDef, getSourceLocationLineNumber(Loc), + unconvertedConditional, convertedConditional); + CurrentHeaderFile->addDirective(directive); + } +} + +void ModularizePPCallbacks::Ifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDirective *MD) { + std::string macroName = MacroNameTok.getIdentifierInfo()->getName().str(); + std::string unconvertedConditional(macroName); + std::string convertedConditional( + macroName + (MD ? " (defined)" : " (not defined)")); + unsigned lineNumber = getSourceLocationLineNumber(Loc); + if (CurrentHeaderFile->getLastLineNumber() < lineNumber) { + ModularizePPDirective directive( + MPPD_IfNDef, getSourceLocationLineNumber(Loc), + unconvertedConditional, convertedConditional); + CurrentHeaderFile->addDirective(directive); + } +} + +// Some utility functions. + +ModularizeHeaderFile *ModularizePPCallbacks::getHeaderFile(const std::string& name) { + ModularizeHeaderFileMap::iterator iter = HeaderFileMap.find(name); + if (iter != HeaderFileMap.end()) + return iter->second; + return NULL; +} + +void ModularizePPCallbacks::addHeaderFile(ModularizeHeaderFile *headerFile) { + HeaderFileMap[headerFile->getName()] = headerFile; +} + +// Add a symbol definition. If the symbol already exists, +// its value is replaced. +void ModularizePPCallbacks::addSymbol(std::string name, std::string value) { + Symbols[name] = value; +} + +// Get symbol value. Return empty string if not defined. +std::string ModularizePPCallbacks::getSymbol(std::string name) { + ModularizeSymbolMapIterator iter = Symbols.find(name); + if (iter != Symbols.end()) + return (*iter).second; + return std::string(); +} + +// Get symbol value. Return true if found. +bool ModularizePPCallbacks::getSymbol(std::string name, std::string &value) { + ModularizeSymbolMapIterator iter = Symbols.find(name); + if (iter != Symbols.end()) { + value = (*iter).second; + return true; + } + return false; +} + +std::string ModularizePPCallbacks::getSourceLocationString(SourceLocation Loc) { + if (Loc.isInvalid()) + return std::string("(none)"); + else + return Loc.printToString(PP.getSourceManager()); +} + +std::string ModularizePPCallbacks::getSourceLocationFile(SourceLocation Loc) { + std::string source(getSourceLocationString(Loc)); + int offset = source.find(':', 2); + if (offset < 0) + return source; + return source.substr(0, offset); +} + +unsigned ModularizePPCallbacks::getSourceLocationLineNumber(SourceLocation Loc) { + return PP.getSourceManager().getPresumedLineNumber(Loc); +} + +// Retrieve source snippet from file image. +std::string ModularizePPCallbacks::getSourceSnippet(SourceRange sourceRange) { + SourceLocation bLoc(sourceRange.getBegin()); + SourceLocation eLoc(sourceRange.getEnd()); + + // Decompose the locations into FID/Offset pairs. + std::pair bLocInfo = PP.getSourceManager().getDecomposedLoc(bLoc); + std::pair eLocInfo = PP.getSourceManager().getDecomposedLoc(eLoc); + FileID FID = bLocInfo.first; + unsigned bFileOffset = bLocInfo.second; + unsigned eFileOffset = eLocInfo.second; + unsigned length = eFileOffset - bFileOffset; + + // Get information about the buffer it points into. + bool Invalid = false; + const char *BufStart = PP.getSourceManager().getBufferData(FID, &Invalid).data(); + if (Invalid) + return std::string(); + + // Rewind from the current position to the start of the line. + const char *bPtr = BufStart + bFileOffset; + + // Trim snippet. + while ((*bPtr <= ' ') && (length != 0)) { + bPtr++; + length--; + } + + while ((length != 0) && (bPtr[length - 1] <= ' ')) + length--; + + std::string returnValue(bPtr, length); + + return returnValue; +} + +// Do macro substitutions in a string. +// FIXME: Note that this is very simple parser, such that it doesn't +// support things like function-style macros, macros with string +// or character literals, and defined(). +// FIXME: There probably should be a function in the Preprocessor +// class that will lex a string, do substitutions, and return either +// a token list or a converted string. +std::string ModularizePPCallbacks::getMacroSubstitutedString(std::string str) { + enum LexMode { + LM_Top, + LM_Symbol + } lexMode = LM_Top; + char output[1024]; + size_t outputLength = 0; + char token[256]; + size_t tokenLength = 0; + std::string symbolValue; + size_t symbolLength; + size_t length = str.length(); + size_t index; + char c = '\0'; + for (index = 0; index <= length; index++) { + if (index < length) + c = str[index]; + else + c = '\0'; +restart: + switch (lexMode) { + case LM_Top: + tokenLength = 0; + if (isalpha(c) || (c == '_')) { + lexMode = LM_Symbol; + goto restart; + } + else { + if (outputLength < sizeof(output) + 1) + output[outputLength++] = c; + } + break; + case LM_Symbol: + if (isalpha(c) || (c == '_') || isdigit(c)) { + if (tokenLength < sizeof(token) - 1) + token[tokenLength++] = c; + } + else { + token[tokenLength] = '\0'; + if (getSymbol(token, symbolValue)) { + // Recursively do substitutions on macro value. + symbolValue = getMacroSubstitutedString(symbolValue); + symbolLength = symbolValue.length(); + if (outputLength + symbolLength < sizeof(output) - 1) { + strcpy(output + outputLength, symbolValue.c_str()); + outputLength += symbolLength; + } + else { + strncpy(output + outputLength, symbolValue.c_str(), sizeof(output) - outputLength - 1); + outputLength = sizeof(output) - 1; + output[sizeof(output) - 1] = '\0'; + } + } + else { + if (outputLength + tokenLength < sizeof(output) - 1) { + strcpy(output + outputLength, token); + outputLength += tokenLength; + } + else { + strncpy(output + outputLength, token, sizeof(output) - outputLength - 1); + outputLength = sizeof(output) - 1; + output[sizeof(output) - 1] = '\0'; + } + } + lexMode = LM_Top; + goto restart; + } + break; + } + } + output[outputLength] = '\0'; + return std::string(output); +} + +} Index: modularize/ModularizeMasterHeaderTracker.cpp =================================================================== --- modularize/ModularizeMasterHeaderTracker.cpp (revision 0) +++ modularize/ModularizeMasterHeaderTracker.cpp (revision 0) @@ -0,0 +1,79 @@ +//==- ModularizeMasterHeaderTracker.cpp - Stores header trackers -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ModularizeMasterHeaderTracker.h" +#include "ModularizeUtilities.h" +#include "llvm/Support/raw_ostream.h" +#include "clang/Basic/SourceManager.h" + +namespace Modularize { +using namespace llvm; +using namespace clang; + +ModularizeMasterHeaderTracker::ModularizeMasterHeaderTracker(bool disabled) + : Disabled(disabled) { +} + +ModularizeMasterHeaderTracker::~ModularizeMasterHeaderTracker() { +} + +// Find an existing header tracker. +// Returns NULL if not found.. +ModularizeHeaderTracker *ModularizeMasterHeaderTracker::getHeaderTracker( + std::string headerName) { + ModularizeHeaderTrackerMap::iterator iter = HeaderTrackers.find(headerName); + if (iter != HeaderTrackers.end()) + return iter->second; + return NULL; +} + +// Process a header snapshot. +// If the header has be seen before, and the snapshot differs, add a new +// header instance. +void ModularizeMasterHeaderTracker::addHeaderFile( + ModularizeHeaderFile *headerFile, std::string topHeader) { + ModularizeHeaderTracker *headerTracker = getHeaderTracker( + headerFile->getName()); + if (headerTracker != NULL) + headerTracker->addHeaderFile(headerFile, topHeader); + else { + headerTracker = new ModularizeHeaderTracker( + headerFile->getName(), headerFile, topHeader); + HeaderTrackers[headerTracker->getName()] = headerTracker; + } +} + +// Report instances where header snapshots differ. +// Returns true if no errors, i.e. no differing snapshots. +bool ModularizeMasterHeaderTracker::report() { + if (Disabled) + return true; + bool returnValue = true; + ModularizeHeaderTrackerMapIterator iter = HeaderTrackers.begin(); + ModularizeHeaderTrackerMapIterator end = HeaderTrackers.end(); + for (; iter != end; ++iter) { + ModularizeHeaderTracker *headerTracker = (*iter).second; + if (!headerTracker->report()) + returnValue = false; + } + return returnValue; +} + +// Display the information for debugging. +void ModularizeMasterHeaderTracker::dump(int level) { + errs() << getIndent(level) << "The headers:\n"; + ModularizeHeaderTrackerMapIterator iter = HeaderTrackers.begin(); + ModularizeHeaderTrackerMapIterator end = HeaderTrackers.end(); + for (; iter != end; ++iter) { + ModularizeHeaderTracker *headerTracker = (*iter).second; + headerTracker->dump(level + 1); + } +} + +} Index: modularize/ModularizeHeaderInstance.h =================================================================== --- modularize/ModularizeHeaderInstance.h (revision 0) +++ modularize/ModularizeHeaderInstance.h (revision 0) @@ -0,0 +1,63 @@ +//===- ModularizeHeaderInstance.h - Stores header instance info-*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines a class to store information about a set of instances of a +/// header being used where the conditional directives are equivalent. +/// +//===----------------------------------------------------------------------===// + +#ifndef MODULARIZEHEADERINSTANCE_H +#define MODULARIZEHEADERINSTANCE_H + +#include "ModularizeHeaderFile.h" + +namespace Modularize { + using namespace clang; + using namespace llvm; + +typedef std::vector ModularizeTopHeaderVector; +typedef ModularizeTopHeaderVector::iterator ModularizeTopHeaderVectorIterator; + +/// \brief This class stores information about a set of instances of a +/// header being used where the conditional directives are equivalent. +class ModularizeHeaderInstance { +public: + ModularizeHeaderInstance(ModularizeHeaderFile *HeaderFile, std::string topHeader); + ~ModularizeHeaderInstance(); + + // Header file accessors. + ModularizeHeaderFile *getHeaderFile() { return HeaderFile; } + void setHeaderFile(ModularizeHeaderFile *headerFile) { HeaderFile = headerFile; }; + + // Top headers (the files directly passed to modularize) accessors. + ModularizeTopHeaderVector &getTopHeaders() { return TopHeaders; } + ModularizeTopHeaderVectorIterator getTopHeadersBegin() { return TopHeaders.begin(); } + ModularizeTopHeaderVectorIterator getTopHeadersEnd() { return TopHeaders.end(); } + bool hasTopHeader(std::string topHeader); + void addTopHeader(std::string topHeader) { TopHeaders.push_back(topHeader); }; + + // Display the information for debugging. + void dump(int level = 0); +private: + // This points to the object containing a unique snapshot of the + // representations of the header preprocessor directives. + ModularizeHeaderFile *HeaderFile; + // This vector contains the names of the top-level headers that + // contained the above unique snapshot. + ModularizeTopHeaderVector TopHeaders; +}; + +// Collections for users of this typs. +typedef std::vector ModularizeHeaderInstanceVector; +typedef ModularizeHeaderInstanceVector::iterator ModularizeHeaderInstanceVectorIterator; + +} // end namespace clang + +#endif Index: modularize/ModularizePPDirective.h =================================================================== --- modularize/ModularizePPDirective.h (revision 0) +++ modularize/ModularizePPDirective.h (revision 0) @@ -0,0 +1,82 @@ +//===--- ModularizePPDirective.h - Represents a PP directive ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines a class to store information about one preprocessor directive +/// that modularize might be interested in. +/// +//===----------------------------------------------------------------------===// + +#ifndef MODULARIZEPPDIRECTIVE_H +#define MODULARIZEPPDIRECTIVE_H + +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceLocation.h" +#include + +namespace Modularize { + using namespace clang; + using namespace llvm; + +// Represents a preprocessor directive kind. +enum ModularizePPDirectiveKind { + MPPD_If, + MPPD_ElIf, + MPPD_IfDef, + MPPD_IfNDef +}; + +/// \brief This class stores information about one preprocessor directive +/// that modularize might be interested in. +class ModularizePPDirective { +public: + ModularizePPDirective( + ModularizePPDirectiveKind directiveKind, unsigned lineNumber, + std::string unconvertedConditional, std::string convertedConditional); + ~ModularizePPDirective(); + + // Source location string accessors. + unsigned getLineNumber() { return LineNumber; } + void setLineNumber(unsigned value) { LineNumber = value; }; + + // Unconverted conditional accessors. + std::string& getUnconvertedConditional() { return UnconvertedConditional; } + void setUnconvertedConditional(std::string value) { UnconvertedConditional = value; }; + + // Converted conditional accessors. + std::string& getConvertedConditional() { return ConvertedConditional; } + void setConvertedConditional(std::string value) { ConvertedConditional = value; }; + + // Return true if this directive matches another. + // (It compares the converted conditionals.) + bool match(ModularizePPDirective *other); + + // Print this directive to a string suitable for a message. + std::string printToString(int level, std::string fileName); + + // Display the information for debugging. + void dump(int level = 0, const char *fileName = NULL); +private: + // The kind of directive. + ModularizePPDirectiveKind DirectiveKind; + // Presumed line number for direcive. + unsigned LineNumber; + // Raw value representation of conditional. + std::string UnconvertedConditional; + // The conditional with macro substitutions done. + std::string ConvertedConditional; +}; + +// Collections for users of this typs. +typedef std::vector ModularizePPDirectiveVector; +typedef ModularizePPDirectiveVector::iterator ModularizePPDirectiveVectorIterator; + +} // end namespace clang + +#endif Index: modularize/ModularizePPCallbacks.h =================================================================== --- modularize/ModularizePPCallbacks.h (revision 0) +++ modularize/ModularizePPCallbacks.h (revision 0) @@ -0,0 +1,90 @@ +//===--- ModularizePPCallbacks.h - Callbacks for Modularize PP -*- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Implements the PPCallbacks interface for modularize. +/// +//===----------------------------------------------------------------------===// + +#ifndef MODULARIZEPPCALLBACKS_H +#define MODULARIZEPPCALLBACKS_H + +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceLocation.h" +#include "ModularizeHeaderFile.h" +#include "ModularizeMasterHeaderTracker.h" + +namespace Modularize { + using namespace clang; + using namespace llvm; + +typedef StringMap ModularizeSymbolMap; +typedef ModularizeSymbolMap::iterator ModularizeSymbolMapIterator; + +/// \brief This class provides callback implementations to grab information +/// about preprocessor directives encountered. +class ModularizePPCallbacks : public PPCallbacks { +public: + ModularizePPCallbacks(Preprocessor &pp, StringRef topFile, + ModularizeMasterHeaderTracker &masterHeaderTracker); + ~ModularizePPCallbacks(); + + // Overidden handlers. + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID = FileID()); + void EndOfMainFile(); + void MacroExpands(const Token &MacroNameTok, const MacroDirective *MD, + SourceRange Range, const MacroArgs *Args); + void If(SourceLocation Loc, SourceRange ConditionRange); + void Elif(SourceLocation Loc, SourceRange ConditionRange, + SourceLocation IfLoc); + void Ifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDirective *MD); + void Ifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDirective *MD); +private: + // Local accessors. + ModularizeHeaderFile *getHeaderFile(const std::string& name); + void addHeaderFile(ModularizeHeaderFile *headerFile); + + // Get symbol map. + ModularizeSymbolMap &getSymbols() { return Symbols; } + // Add a symbol definition. If the symbol already exists, + // its value is replaced. + void addSymbol(std::string name, std::string value); + // Get symbol value. Return empty string if not defined. + std::string getSymbol(std::string name); + // Get symbol value. Return true if found. + bool getSymbol(std::string name, std::string &value); + + // Local utilities. + std::string getSourceLocationString(SourceLocation Loc); + std::string getSourceLocationFile(SourceLocation Loc); + unsigned getSourceLocationLineNumber(SourceLocation Loc); + // Retrieve source snippet from file image. + std::string getSourceSnippet(SourceRange sourceRange); + // Do macro substitutions in a string. + std::string getMacroSubstitutedString(std::string str); + +private: + // Local data. + ModularizeMasterHeaderTracker &MasterHeaderTracker; + std::string TopFile; + Preprocessor &PP; + ModularizeHeaderFile *RootHeaderFile; + ModularizeHeaderFile *CurrentHeaderFile; + ModularizeHeaderFileMap HeaderFileMap; + ModularizeSymbolMap Symbols; + +}; + +} // end namespace clang + +#endif Index: modularize/ModularizeMasterHeaderTracker.h =================================================================== --- modularize/ModularizeMasterHeaderTracker.h (revision 0) +++ modularize/ModularizeMasterHeaderTracker.h (revision 0) @@ -0,0 +1,60 @@ +//===- ModularizeMasterHeaderTracker.h - Stores header trackers -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines a class to store all the header trackers, for use in finding +/// header instance differences. +/// +//===----------------------------------------------------------------------===// + +#ifndef MODULARIZEMASTERHEADERTRACKER_H +#define MODULARIZEMASTERHEADERTRACKER_H + +#include "ModularizeHeaderFile.h" +#include "ModularizeHeaderTracker.h" + +namespace Modularize { + using namespace clang; + using namespace llvm; + +/// \brief This class stores all the header trackers, for use in finding +/// header instance differences. +class ModularizeMasterHeaderTracker { +public: + ModularizeMasterHeaderTracker(bool disabled); + ~ModularizeMasterHeaderTracker(); + + // Return true if header preprocessing checking is disabled. + bool isDisabled() { return Disabled; } + + // Return the map of all the header trackers. + ModularizeHeaderTrackerMap& getHeaderTrackers() { return HeaderTrackers; } + // Find an existing header tracker. + // Returns NULL if not found.. + ModularizeHeaderTracker *getHeaderTracker(std::string headerName); + // Process a header snapshot. + // If the header has be seen before, and the snapshot differs, add a new + // header instance. + void addHeaderFile(ModularizeHeaderFile *headerFile, std::string topHeader); + + // Report instances where header snapshots differ. + // Returns true if no errors, i.e. no differing snapshots. + bool report(); + // Display the information for debugging. + void dump(int level = 0); +private: + // Set to true if header preprocessing checking is disabled. + bool Disabled; + // This map contains an entry for each header file encountered. + ModularizeHeaderTrackerMap HeaderTrackers; +}; + +} // end namespace clang + +#endif Index: modularize/ModularizeHeaderFile.cpp =================================================================== --- modularize/ModularizeHeaderFile.cpp (revision 0) +++ modularize/ModularizeHeaderFile.cpp (revision 0) @@ -0,0 +1,63 @@ +//===--- ModularizeHeaderFile.cpp - Callbacks for Preprocessor actions ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ModularizeHeaderFile.h" +#include "ModularizeUtilities.h" +#include "llvm/Support/raw_ostream.h" +#include "clang/Basic/SourceManager.h" + +namespace Modularize { +using namespace llvm; +using namespace clang; + +ModularizeHeaderFile::ModularizeHeaderFile(std::string name, Preprocessor& pp) : + Name(name) { +} + +ModularizeHeaderFile::~ModularizeHeaderFile() { +} + +// Get highest line-numbered directive. +unsigned ModularizeHeaderFile::getLastLineNumber() { + if (Directives.size() == 0) + return 0; + return Directives[Directives.size() - 1].getLineNumber(); +} + +// Return true if this snapshot matches the other snapshot. +bool ModularizeHeaderFile::match(ModularizeHeaderFile *other) { + bool returnValue = true; + // Walk conditional directives. If the condition + // (after macro substitutions) is not the same, + // return false, meaning the header file snapshot doesn't match. + ModularizePPDirectiveVectorIterator thisIter = getDirectivesBegin(); + ModularizePPDirectiveVectorIterator thisEnd = getDirectivesEnd(); + ModularizePPDirectiveVectorIterator otherIter = other->getDirectivesBegin(); + ModularizePPDirectiveVectorIterator otherEnd = other->getDirectivesEnd(); + for (; (thisIter != thisEnd) && (otherIter != otherEnd); + ++thisIter, ++ otherIter) { + if (!(*thisIter).match(&(*otherIter))) { + returnValue = false; + break; + } + } + return returnValue; +} + +// Display the information for debugging. +void ModularizeHeaderFile::dump(int level) { + errs() << getIndent(level) << "preprocessor directives:\n"; + ModularizePPDirectiveVectorIterator dIter = getDirectivesBegin(); + ModularizePPDirectiveVectorIterator dEnd = getDirectivesEnd(); + for (; dIter != dEnd; ++dIter) { + (*dIter).dump(level + 1, Name.c_str()); + } +} + +} Index: modularize/ModularizeHeaderFile.h =================================================================== --- modularize/ModularizeHeaderFile.h (revision 0) +++ modularize/ModularizeHeaderFile.h (revision 0) @@ -0,0 +1,75 @@ +//===--- ModularizeHeaderFile.h - Stores PP directives ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines a class to store information about preprocessor directives +/// encountered in a header file which might be relevant to finding modules-related +/// problems. +/// +//===----------------------------------------------------------------------===// + +#ifndef MODULARIZEHEADERFILE_H +#define MODULARIZEHEADERFILE_H + +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/StringMap.h" +#include "ModularizePPDirective.h" +#include + +namespace Modularize { + using namespace clang; + using namespace llvm; + +/// \brief This class stores information about preprocessor directives +/// encountered in a header file which might be relevant to finding +/// modules-related problems. +class ModularizeHeaderFile { +public: + ModularizeHeaderFile(std::string name, Preprocessor &pp); + ~ModularizeHeaderFile(); + + // Name accessors. + std::string& getName() { return Name; } + void setName(std::string name) { Name = name; }; + + // Preprocessor directive accessors. + ModularizePPDirectiveVector &getDirectives() { return Directives; } + int getDirectiveCount() { return Directives.size(); } + ModularizePPDirectiveVectorIterator getDirectivesBegin() + { return Directives.begin(); } + ModularizePPDirectiveVectorIterator getDirectivesEnd() + { return Directives.end(); } + ModularizePPDirective &getDirective(int index) { return Directives[index]; } + void addDirective(ModularizePPDirective &directive) + { Directives.push_back(directive); }; + + // Get highest line-numbered directive. + unsigned getLastLineNumber(); + + // Return true if this snapshot matches the other snapshot. + bool match(ModularizeHeaderFile *other); + + // Display the information for debugging. + void dump(int level = 0); +private: + // The full path name of the header file. + std::string Name; + // The array of preprocessor directives in the file, limited + // to the kinds modularize is interested in. + ModularizePPDirectiveVector Directives; +}; + +// Collections for users of this typs. +typedef StringMap ModularizeHeaderFileMap; +typedef ModularizeHeaderFileMap::iterator ModularizeHeaderFileMapIterator; + +} // end namespace clang + +#endif Index: modularize/CMakeLists.txt =================================================================== --- modularize/CMakeLists.txt (revision 183718) +++ modularize/CMakeLists.txt (working copy) @@ -7,6 +7,13 @@ add_clang_executable(modularize Modularize.cpp + ModularizeHeaderFile.cpp + ModularizeHeaderInstance.cpp + ModularizeHeaderTracker.cpp + ModularizeMasterHeaderTracker.cpp + ModularizePPCallbacks.cpp + ModularizePPDirective.cpp + ModularizeUtilities.cpp ) target_link_libraries(modularize