Index: test/modularize/Inputs/InconsistentSubHeader.h =================================================================== --- test/modularize/Inputs/InconsistentSubHeader.h (revision 184221) +++ 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 184221) +++ test/modularize/ProblemsInconsistent.modularize (working copy) @@ -6,6 +6,15 @@ # CHECK: error: macro 'SYMBOL' defined at multiple locations: # CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9 # CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:6:9 +# 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 Index: modularize/HeaderTracker.cpp =================================================================== --- modularize/HeaderTracker.cpp (revision 0) +++ modularize/HeaderTracker.cpp (revision 0) @@ -0,0 +1,154 @@ +//==- HeaderTracker.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 "HeaderTracker.h" +#include "Utilities.h" +#include "llvm/Support/raw_ostream.h" + +namespace Modularize { +using namespace llvm; +using namespace clang; + +HeaderTracker::HeaderTracker( + std::string& name, HeaderFile *headerFile, + std::string topHeader) : + Name(name) { + HeaderInstance *headerInstance = new HeaderInstance( + headerFile, topHeader); + addHeaderInstance(headerInstance); +} + +HeaderTracker::~HeaderTracker() { + deleteHeaderInstances(); +} + +// Process a header snapshot. +// If the snapshot differs, add a new header instance. +void HeaderTracker::addHeaderFile( + HeaderFile *headerFile, std::string topHeader) { + HeaderInstanceVectorIterator iter = HeaderInstances.begin(); + HeaderInstanceVectorIterator EI = HeaderInstances.end(); + for (; iter != EI; ++iter) { + HeaderFile *existingHeader = (*iter)->getHeaderFile(); + if (existingHeader->match(headerFile)) { + (*iter)->addTopHeader(topHeader); + delete headerFile; // No longer needed. + return; + } + } + HeaderInstance *newInstance = new HeaderInstance( + headerFile, topHeader); + addHeaderInstance(newInstance); +} + +// Add a header instance. +void HeaderTracker::addHeaderInstance( + HeaderInstance *headerInstance) { + HeaderInstances.push_back(headerInstance); +} + +// Delete all the header instances. +void HeaderTracker::deleteHeaderInstances() { + HeaderInstanceVectorIterator hiIter = getHeaderInstancesBegin(); + HeaderInstanceVectorIterator hiEnd = getHeaderInstancesEnd(); + for (; hiIter != hiEnd; ++hiIter) + delete (*hiIter); + HeaderInstances.clear(); +} + +// Report instances where header snapshots differ. +// Returns true if no errors, i.e. no differing snapshots. +bool HeaderTracker::report() { + int headerInstanceCount = getHeaderInstanceCount(); + if (headerInstanceCount <= 1) + return true; // Early exit. No errors. + HeaderInstanceVectorIterator hiIter = getHeaderInstancesBegin(); + HeaderInstanceVectorIterator hiEnd = getHeaderInstancesEnd(); + int directiveCount = 0x7fffffff; // Big number. + // Get the number of instances which is least. + for (; hiIter != hiEnd; ++hiIter) { + HeaderInstance *headerInstance = (*hiIter); + HeaderFile *headerFile = headerInstance->getHeaderFile(); + if (headerFile->getDirectiveCount() < directiveCount) + directiveCount = headerFile->getDirectiveCount(); + } + // Walk the directive vectors of the instance in parallel. + for (int directiveIndex = 0; directiveIndex < directiveCount; + directiveIndex++) { + HeaderFile *firstHeaderFile = + getHeaderInstance(0)->getHeaderFile(); + PreprocessorDirective *masterDirective = + &firstHeaderFile->getDirective(directiveIndex); + bool mismatch = false; + // Check the i'th directive of all the instances. + for (int headerInstanceIndex = 1; + headerInstanceIndex < headerInstanceCount; + headerInstanceIndex++) { + HeaderInstance *headerInstance = + getHeaderInstance(headerInstanceIndex); + HeaderFile *headerFile = headerInstance->getHeaderFile(); + PreprocessorDirective *directive = + &headerFile->getDirective(directiveIndex); + // If it matches the master, continue on to the next one. + if (directive->match(masterDirective)) + continue; + mismatch = true; + } + // If we had no mismatches, continue on to next directive. + if (!mismatch) + continue; + // We had one or more mismatches. Output diagnostic and exit. + errs() << "warning: The instances of header " + << Name << " have different contents after preprocessing:\n"; + for (int headerInstanceIndex = 0; + headerInstanceIndex < headerInstanceCount; + headerInstanceIndex++) { + HeaderInstance *headerInstance = + getHeaderInstance(headerInstanceIndex); + HeaderFile *headerFile = headerInstance->getHeaderFile(); + errs() << getIndent(1) + << "When included or nested in these top-level headers:\n"; + TopHeaderVectorIterator thIter = + headerInstance->getTopHeadersBegin(); + TopHeaderVectorIterator 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"; + PreprocessorDirective *directive = + &headerFile->getDirective(directiveIndex); + directive->dump(2, Name.c_str()); + } + break; // Break out now that we've found directives that don't match, + // as the directives list might not match anymore. + } + return false; // There was a mismatch. +} + +// Display the information for debugging. +void HeaderTracker::dump(int level) { + errs() << getIndent(level) << Name << ":\n"; + errs() << getIndent(level + 1) << "instance count: " + << getHeaderInstanceCount() << "\n"; + + errs() << getIndent(level + 1) << "instances: \n"; + HeaderInstanceVectorIterator hiIter = getHeaderInstancesBegin(); + HeaderInstanceVectorIterator hiEnd = getHeaderInstancesEnd(); + int hiIndex; + for (hiIndex = 0; hiIter != hiEnd; ++hiIter, ++hiIndex) { + HeaderInstance *headerInstance = (*hiIter); + errs() << getIndent(level + 2) << "instance: " << hiIndex << "\n"; + headerInstance->dump(level + 3); + } +} + +} + Index: modularize/HeaderTracker.h =================================================================== --- modularize/HeaderTracker.h (revision 0) +++ modularize/HeaderTracker.h (revision 0) @@ -0,0 +1,80 @@ +//===- HeaderTracker.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 HEADER_TRACKER_H +#define HEADER_TRACKER_H + +#include "HeaderInstance.h" + +namespace Modularize { + +/// \brief Stores the header snapshot instances for a particular header. +/// +/// This class tracks the instances of one particular header. It stores the +/// header name and a vector of HeaderInstance’s. If all instances +/// of a header seen have the same conditionals after preprocessing, there will +/// only be one HeaderInstance. If one or more conditionals were +/// different, there will be two or more instance objects saved. +class HeaderTracker { +public: + HeaderTracker(std::string &name, HeaderFile *headerFile, + std::string topHeader); + ~HeaderTracker(); + + // 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. + // Takes ownership of headerFile. + void addHeaderFile(HeaderFile *headerFile, std::string topHeader); + // Add a header instance. + // Takes ownership of headerInstance. + void addHeaderInstance(HeaderInstance *headerInstance); + // Get instance count. > 1 means there were mismatches. + int getHeaderInstanceCount() { return HeaderInstances.size(); } + // Get header instance vector. + HeaderInstanceVector &getHeaderInstances() { return HeaderInstances; } + // Get header instance vector start iterator. + HeaderInstanceVectorIterator getHeaderInstancesBegin() + { return HeaderInstances.begin(); } + // Get header instance vector end iterator. + HeaderInstanceVectorIterator getHeaderInstancesEnd() + { return HeaderInstances.end(); } + // Get header instance by index. + HeaderInstance *getHeaderInstance(int index) + { return HeaderInstances[index]; } + // Delete all the header instances. + void deleteHeaderInstances(); + + // 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; + HeaderInstanceVector HeaderInstances; +}; + +// Collections for users of this typs. +typedef llvm::StringMap HeaderTrackerMap; +typedef HeaderTrackerMap::iterator HeaderTrackerMapIterator; + +} // end namespace clang + +#endif Index: modularize/Utilities.cpp =================================================================== --- modularize/Utilities.cpp (revision 0) +++ modularize/Utilities.cpp (revision 0) @@ -0,0 +1,36 @@ +//===--- Utilities.cpp - common utilities -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------===// + +#include "Utilities.h" +#include "llvm/Support/raw_ostream.h" +#include "clang/Basic/SourceManager.h" +#include +#include + +namespace Modularize { +using namespace llvm; +using namespace clang; + +std::string getIndent(int level) { + char buffer[256]; + int index = 0; + + while (level > 0) { + int size = 4; + while (size-- > 0) + buffer[index++] = ' '; + level--; + } + + buffer[index] = '\0'; + + return std::string(buffer); +} + +} Index: modularize/Modularize.cpp =================================================================== --- modularize/Modularize.cpp (revision 184347) +++ modularize/Modularize.cpp (working copy) @@ -5,7 +5,7 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===----------------------------------------------------------------------===// +//===--------------------------------------------------------------------===// // // This file implements a tool that checks whether a set of headers provides // the consistent definitions required to use modules. For example, it detects @@ -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,21 +69,21 @@ // // 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" #include "clang/AST/ASTContext.h" @@ -90,10 +106,13 @@ #include #include #include +#include "MasterHeaderTracker.h" +#include "PreprocessorCallbacks.h" using namespace clang::tooling; using namespace clang; using namespace llvm; +using namespace Modularize; // Option to specify a file name for a list of header files to check. cl::opt @@ -112,6 +131,15 @@ " If not specified," " the files are considered to be relative to the header list file.")); +// 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)); + // Read the header list file and collect the header file names. error_code getHeaderFileNames(SmallVectorImpl &headerFileNames, StringRef listFileName, StringRef headerPrefix) { @@ -380,8 +408,15 @@ class CollectEntitiesConsumer : public ASTConsumer { public: - CollectEntitiesConsumer(EntityMap &Entities, Preprocessor &PP) - : Entities(Entities), PP(PP) {} + CollectEntitiesConsumer( + EntityMap &Entities, + MasterHeaderTracker &masterTracker, + Preprocessor &PP, StringRef InFile) + : Entities(Entities), MasterTracker(masterTracker), PP(PP) { + if (!masterTracker.isDisabled()) + PP.addPPCallbacks( + new PreprocessorCallbacks(PP, InFile, masterTracker)); + } virtual void HandleTranslationUnit(ASTContext &Ctx) { SourceManager &SM = Ctx.getSourceManager(); @@ -406,30 +441,39 @@ } private: EntityMap &Entities; + MasterHeaderTracker &MasterTracker; Preprocessor &PP; }; class CollectEntitiesAction : public SyntaxOnlyAction { public: - CollectEntitiesAction(EntityMap &Entities) : Entities(Entities) {} + CollectEntitiesAction( + EntityMap &Entities, MasterHeaderTracker &masterTracker) + : Entities(Entities), MasterTracker(masterTracker) {} protected: virtual clang::ASTConsumer * CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - return new CollectEntitiesConsumer(Entities, CI.getPreprocessor()); + return new CollectEntitiesConsumer( + Entities, MasterTracker, CI.getPreprocessor(), InFile); } private: EntityMap &Entities; + MasterHeaderTracker &MasterTracker; }; class ModularizeFrontendActionFactory : public FrontendActionFactory { public: - ModularizeFrontendActionFactory(EntityMap &Entities) : Entities(Entities) {} + ModularizeFrontendActionFactory( + EntityMap &Entities, + MasterHeaderTracker &masterTracker) + : Entities(Entities), MasterTracker(masterTracker) {} virtual CollectEntitiesAction *create() { - return new CollectEntitiesAction(Entities); + return new CollectEntitiesAction(Entities, MasterTracker); } private: EntityMap &Entities; + MasterHeaderTracker &MasterTracker; }; int main(int argc, const char **argv) { @@ -458,10 +502,14 @@ Compilations.reset( new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments)); + // Create the master header tracker. + MasterHeaderTracker 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 Index: modularize/HeaderInstance.cpp =================================================================== --- modularize/HeaderInstance.cpp (revision 0) +++ modularize/HeaderInstance.cpp (revision 0) @@ -0,0 +1,60 @@ +//==- HeaderInstance.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 "HeaderInstance.h" +#include "Utilities.h" +#include "llvm/Support/raw_ostream.h" +#include "clang/Basic/SourceManager.h" + +namespace Modularize { +using namespace llvm; +using namespace clang; + +HeaderInstance::HeaderInstance( + HeaderFile *headerFile, std::string topHeader) : + TheHeaderFile(headerFile) { + TopHeaders.push_back(topHeader); +} + +HeaderInstance::~HeaderInstance() { + delete TheHeaderFile; + TheHeaderFile = NULL; +} + +bool HeaderInstance::hasTopHeader(std::string topHeader) { + TopHeaderVectorIterator iter = TopHeaders.begin(); + TopHeaderVectorIterator end = TopHeaders.begin(); + for (; iter != end; ++iter) { + if (*iter == topHeader) + return true; + } + return false; +} + +// Display the information for debugging. +void HeaderInstance::dump(int level) { + errs() << getIndent(level) << "top headers:\n"; + + TopHeaderVectorIterator thIter = getTopHeadersBegin(); + TopHeaderVectorIterator thEnd = getTopHeadersEnd(); + for (; thIter != thEnd; ++thIter) { + errs() << getIndent(level + 1) << *thIter << "\n"; + } + + errs() << getIndent(level) << "preprocessor directives: \n"; + PreprocessorDirectiveVectorIterator dIter = + TheHeaderFile->getDirectivesBegin(); + PreprocessorDirectiveVectorIterator dEnd = + TheHeaderFile->getDirectivesEnd(); + for (; dIter != dEnd; ++dIter) { + dIter->dump(level + 1, TheHeaderFile->getName().c_str()); + } +} + +} Index: modularize/Utilities.h =================================================================== --- modularize/Utilities.h (revision 0) +++ modularize/Utilities.h (revision 0) @@ -0,0 +1,27 @@ +//===--- Utilities.h - Common utilities ---------------*- 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 utilities used in common for Modularize. +/// +//===--------------------------------------------------------------------===// + +#ifndef UTILITIES_H +#define UTILITIES_H + +#include + +namespace Modularize { + +/// \brief Returns a string with level * 4 spaces. +std::string getIndent(int level); + +} // end namespace clang + +#endif Index: modularize/MasterHeaderTracker.cpp =================================================================== --- modularize/MasterHeaderTracker.cpp (revision 0) +++ modularize/MasterHeaderTracker.cpp (revision 0) @@ -0,0 +1,89 @@ +//==- MasterHeaderTracker.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 "MasterHeaderTracker.h" +#include "Utilities.h" +#include "llvm/Support/raw_ostream.h" +#include "clang/Basic/SourceManager.h" + +namespace Modularize { +using namespace llvm; +using namespace clang; + +MasterHeaderTracker::MasterHeaderTracker(bool disabled) + : Disabled(disabled) { +} + +MasterHeaderTracker::~MasterHeaderTracker() { + deleteHeaderTrackers(); +} + +// Find an existing header tracker. +// Returns NULL if not found.. +HeaderTracker *MasterHeaderTracker::getHeaderTracker( + std::string headerName) { + HeaderTrackerMap::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 MasterHeaderTracker::addHeaderFile( + HeaderFile *headerFile, std::string topHeader) { + HeaderTracker *headerTracker = getHeaderTracker( + headerFile->getName()); + if (headerTracker != NULL) + headerTracker->addHeaderFile(headerFile, topHeader); + else { + headerTracker = new HeaderTracker( + headerFile->getName(), headerFile, topHeader); + HeaderTrackers[headerTracker->getName()] = headerTracker; + } +} + +// Delete all the header trackers. +void MasterHeaderTracker::deleteHeaderTrackers() { + HeaderTrackerMapIterator hiIter = HeaderTrackers.begin(); + HeaderTrackerMapIterator hiEnd = HeaderTrackers.end(); + for (; hiIter != hiEnd; ++hiIter) + delete hiIter->second; + HeaderTrackers.clear(); +} + +// Report instances where header snapshots differ. +// Returns true if no errors, i.e. no differing snapshots. +bool MasterHeaderTracker::report() { + if (Disabled) + return true; + bool returnValue = true; + HeaderTrackerMapIterator iter = HeaderTrackers.begin(); + HeaderTrackerMapIterator end = HeaderTrackers.end(); + for (; iter != end; ++iter) { + HeaderTracker *headerTracker = iter->second; + if (!headerTracker->report()) + returnValue = false; + } + return returnValue; +} + +// Display the information for debugging. +void MasterHeaderTracker::dump(int level) { + errs() << getIndent(level) << "The headers:\n"; + HeaderTrackerMapIterator iter = HeaderTrackers.begin(); + HeaderTrackerMapIterator end = HeaderTrackers.end(); + for (; iter != end; ++iter) { + HeaderTracker *headerTracker = iter->second; + headerTracker->dump(level + 1); + } +} + +} Index: modularize/HeaderInstance.h =================================================================== --- modularize/HeaderInstance.h (revision 0) +++ modularize/HeaderInstance.h (revision 0) @@ -0,0 +1,73 @@ +//===- HeaderInstance.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 HEADER_INSTANCE_H +#define HEADER_INSTANCE_H + +#include "HeaderFile.h" + +namespace Modularize { + +typedef std::vector TopHeaderVector; +typedef TopHeaderVector::iterator TopHeaderVectorIterator; + +/// \brief Stores information about a set of equivalent header instances. +/// +/// This class stores a pointer to a HeaderFile for a header, and a +/// vector of header file names for the headers from the modularize header list +/// that reference the particular header, either directly or indirectly via +/// some nested include. If separate instances of the header are encountered +/// when modularize processes its header list, if the preprocessed directive +/// conditionals stored in the clang::tok::PPKeywordKind vector are the same for +/// and existing HeaderFile object, the top-level header name is +/// added to the instance, effectively reusing the HeaderFile object. +/// If a header is seen for the first time, or if the preprocessed conditionals +/// for the stored directives don’t match those of an instance of the header +/// seen before, a new HeaderInstance object is created and saved. +class HeaderInstance { +public: + HeaderInstance(HeaderFile *HeaderFile, std::string topHeader); + ~HeaderInstance(); + + // Header file accessors. + HeaderFile *getHeaderFile() { return TheHeaderFile; } + void setHeaderFile(HeaderFile *headerFile) { TheHeaderFile = headerFile; }; + + // Top headers (the files directly passed to modularize) accessors. + TopHeaderVector &getTopHeaders() { return TopHeaders; } + TopHeaderVectorIterator getTopHeadersBegin() { return TopHeaders.begin(); } + TopHeaderVectorIterator 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. + HeaderFile *TheHeaderFile; + // This vector contains the names of the top-level headers that + // contained the above unique snapshot. + TopHeaderVector TopHeaders; +}; + +// Collections for users of this typs. +typedef std::vector HeaderInstanceVector; +typedef HeaderInstanceVector::iterator HeaderInstanceVectorIterator; + +} // end namespace clang + +#endif Index: modularize/PreprocessorDirective.cpp =================================================================== --- modularize/PreprocessorDirective.cpp (revision 0) +++ modularize/PreprocessorDirective.cpp (revision 0) @@ -0,0 +1,89 @@ +//===--- PreprocessorDirective.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 "PreprocessorDirective.h" +#include "Utilities.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/raw_ostream.h" + +namespace Modularize { +using namespace llvm; +using namespace clang; + +PreprocessorDirective::PreprocessorDirective( + clang::tok::PPKeywordKind directiveKind, unsigned lineNumber, + std::string unconvertedConditional, std::string convertedConditional) : + DirectiveKind(directiveKind), + LineNumber(lineNumber), + UnconvertedConditional(unconvertedConditional), + ConvertedConditional(convertedConditional) { +} + +PreprocessorDirective::~PreprocessorDirective() { +} + +// Return true if this directive matches another. +// (It compares the converted conditionals.) +bool PreprocessorDirective::match(PreprocessorDirective *other) { + if (ConvertedConditional == other->getConvertedConditional()) { + return true; + } + return false; +} + +// Print this directive to a stream, suitable for a message. +void PreprocessorDirective::print(llvm::raw_ostream &OS, + int level, std::string fileName) { + const char *directiveSpelling = getDirectiveSpelling(DirectiveKind); + OS << getIndent(level) << fileName << ":" << LineNumber << ":1: #" + << directiveSpelling << " " << UnconvertedConditional << " (#" << + directiveSpelling << " " << ConvertedConditional << ")\n"; +} + +// Print this directive to a string, suitable for a message. +std::string PreprocessorDirective::printToString( + int level, std::string fileName) { + std::string S; + llvm::raw_string_ostream OS(S); + print(OS, level, fileName); + return OS.str(); +} + +// Display useful information about this object for debugging. +void PreprocessorDirective::dump(int level, const char *fileName) { + if (fileName == NULL) + fileName = "line"; + print(errs(), level, fileName); +} + +// Get directive spelling. +const char *PreprocessorDirective::getDirectiveSpelling( + clang::tok::PPKeywordKind kind) { + const char *directiveString; + switch (kind) { + case clang::tok::pp_if: + directiveString = "if"; + break; + case clang::tok::pp_elif: + directiveString = "elif"; + break; + case clang::tok::pp_ifdef: + directiveString = "ifdef"; + break; + case clang::tok::pp_ifndef: + directiveString = "ifndef"; + break; + default: + directiveString = "(unknown)"; + break; + } + return directiveString; +} + +} Index: modularize/MasterHeaderTracker.h =================================================================== --- modularize/MasterHeaderTracker.h (revision 0) +++ modularize/MasterHeaderTracker.h (revision 0) @@ -0,0 +1,65 @@ +//===- MasterHeaderTracker.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 MASTER_HEADER_TRACKER_H +#define MASTER_HEADER_TRACKER_H + +#include "HeaderFile.h" +#include "HeaderTracker.h" + +namespace Modularize { + +/// \brief Stores all header trackers for finding header instance differences. +/// +/// This class stores a map of all the HeaderTracker objects, and +/// provides an "addHeaderFile" function for handling a header file, and a +/// "report" function for outputting the warnings about the preprocessor +/// conditional directive mismatches. +class MasterHeaderTracker { +public: + MasterHeaderTracker(bool disabled); + ~MasterHeaderTracker(); + + // Return true if header preprocessing checking is disabled. + bool isDisabled() { return Disabled; } + + // Return the map of all the header trackers. + HeaderTrackerMap &getHeaderTrackers() { return HeaderTrackers; } + // Find an existing header tracker. + // Returns NULL if not found.. + HeaderTracker *getHeaderTracker(std::string headerName); + // Process a header snapshot. + // If the header has be seen before, and the snapshot differs, add a new + // header instance. + // Takes ownership of headerFile. + void addHeaderFile(HeaderFile *headerFile, std::string topHeader); + // Delete all the header trackers. + void deleteHeaderTrackers(); + + // 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. + HeaderTrackerMap HeaderTrackers; +}; + +} // end namespace clang + +#endif Index: modularize/PreprocessorCallbacks.cpp =================================================================== --- modularize/PreprocessorCallbacks.cpp (revision 0) +++ modularize/PreprocessorCallbacks.cpp (revision 0) @@ -0,0 +1,302 @@ +//===--- 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 "PreprocessorCallbacks.h" +#include "Utilities.h" +#include "llvm/Support/raw_ostream.h" +#include "clang/Basic/SourceManager.h" +#include +#include +#include + +namespace Modularize { +using namespace llvm; +using namespace clang; + +PreprocessorCallbacks::PreprocessorCallbacks(Preprocessor& pp, StringRef topFile, + MasterHeaderTracker &masterHeaderTracker) : + MasterTracker(masterHeaderTracker), + TopFile(topFile.str()), + PP(pp), + RootHeaderFile(NULL), + CurrentHeaderFile(NULL) { + HeaderFile *mhf = new HeaderFile(topFile.str(), PP); + addHeaderFile(mhf); // Takes ownership. + CurrentHeaderFile = mhf; + RootHeaderFile = mhf; +} + +PreprocessorCallbacks::~PreprocessorCallbacks() { +} + +void PreprocessorCallbacks::FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) { + std::string locgetFileName(getSourceLocationFile(Loc)); + HeaderFile *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 HeaderFile(*fileName, PP); + addHeaderFile(mhf); + CurrentHeaderFile = mhf; + if (!RootHeaderFile) + RootHeaderFile = mhf; + } + else + CurrentHeaderFile = mhf; +} + +void PreprocessorCallbacks::EndOfMainFile() { + // Add all the header snapshots to the master header file tracker. + HeaderFileMapIterator iter = HeadersMap.begin(); + HeaderFileMapIterator end = HeadersMap.end(); + for (; iter != end; ++iter) { + MasterTracker.addHeaderFile(iter->second, TopFile); + } +} + +void PreprocessorCallbacks::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 PreprocessorCallbacks::If(SourceLocation Loc, SourceRange ConditionRange) { + std::string unconvertedConditional(getSourceSnippet(ConditionRange)); + std::string convertedConditional(getMacroSubstitutedString(unconvertedConditional)); + unsigned lineNumber = getSourceLocationLineNumber(Loc); + if (CurrentHeaderFile->getLastLineNumber() < lineNumber) { + PreprocessorDirective directive( + clang::tok::pp_if, lineNumber, + unconvertedConditional, convertedConditional); + CurrentHeaderFile->addDirective(directive); + } +} + +void PreprocessorCallbacks::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) { + PreprocessorDirective directive( + clang::tok::pp_elif, getSourceLocationLineNumber(Loc), + unconvertedConditional, convertedConditional); + CurrentHeaderFile->addDirective(directive); + } +} + +void PreprocessorCallbacks::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) { + PreprocessorDirective directive( + clang::tok::pp_ifdef, getSourceLocationLineNumber(Loc), + unconvertedConditional, convertedConditional); + CurrentHeaderFile->addDirective(directive); + } +} + +void PreprocessorCallbacks::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) { + PreprocessorDirective directive( + clang::tok::pp_ifndef, getSourceLocationLineNumber(Loc), + unconvertedConditional, convertedConditional); + CurrentHeaderFile->addDirective(directive); + } +} + +// Some utility functions. + +HeaderFile *PreprocessorCallbacks::getHeaderFile(const std::string& name) { + HeaderFileMap::iterator iter = HeadersMap.find(name); + if (iter != HeadersMap.end()) + return iter->second; + return NULL; +} + +void PreprocessorCallbacks::addHeaderFile(HeaderFile *headerFile) { + HeadersMap[headerFile->getName()] = headerFile; +} + +// Add a symbol definition. If the symbol already exists, +// its value is replaced. +void PreprocessorCallbacks::addSymbol(std::string name, std::string value) { + Symbols[name] = value; +} + +// Get symbol value. Return empty string if not defined. +std::string PreprocessorCallbacks::getSymbol(std::string name) { + SymbolMapIterator iter = Symbols.find(name); + if (iter != Symbols.end()) + return iter->second; + return std::string(); +} + +// Get symbol value. Return true if found. +bool PreprocessorCallbacks::getSymbol(std::string name, std::string &value) { + SymbolMapIterator iter = Symbols.find(name); + if (iter != Symbols.end()) { + value = iter->second; + return true; + } + return false; +} + +std::string PreprocessorCallbacks::getSourceLocationString(SourceLocation Loc) { + if (Loc.isInvalid()) + return std::string("(none)"); + else + return Loc.printToString(PP.getSourceManager()); +} + +std::string PreprocessorCallbacks::getSourceLocationFile(SourceLocation Loc) { + std::string source(getSourceLocationString(Loc)); + int offset = source.find(':', 2); + if (offset < 0) + return source; + return source.substr(0, offset); +} + +unsigned PreprocessorCallbacks::getSourceLocationLineNumber(SourceLocation Loc) { + return PP.getSourceManager().getPresumedLineNumber(Loc); +} + +// Retrieve source snippet from file image. +std::string PreprocessorCallbacks::getSourceSnippet(SourceRange sourceRange) { + SourceLocation bLoc(sourceRange.getBegin()); + SourceLocation eLoc(sourceRange.getEnd()); + const char *bPtr = PP.getSourceManager().getCharacterData(bLoc); + const char *ePtr = PP.getSourceManager().getCharacterData(eLoc); + size_t length = ePtr - bPtr; + return StringRef(bPtr, length).trim().str(); +} + +// 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. However, it's somewhat +// problematic in that it could interfere with the preprocessor +// and lexor state, and call the MacroExpanded handlers again. +// So for now, we stick with this simple function, to be extend +// to support function-style macros. +std::string PreprocessorCallbacks::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/HeaderFile.cpp =================================================================== --- modularize/HeaderFile.cpp (revision 0) +++ modularize/HeaderFile.cpp (revision 0) @@ -0,0 +1,63 @@ +//===--- HeaderFile.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 "HeaderFile.h" +#include "Utilities.h" +#include "llvm/Support/raw_ostream.h" +#include "clang/Basic/SourceManager.h" + +namespace Modularize { +using namespace llvm; +using namespace clang; + +HeaderFile::HeaderFile(std::string name, clang::Preprocessor& pp) : + Name(name) { +} + +HeaderFile::~HeaderFile() { +} + +// Get highest line-numbered directive. +unsigned HeaderFile::getLastLineNumber() { + if (Directives.size() == 0) + return 0; + return Directives[Directives.size() - 1].getLineNumber(); +} + +// Return true if this snapshot matches the other snapshot. +bool HeaderFile::match(HeaderFile *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. + PreprocessorDirectiveVectorIterator thisIter = getDirectivesBegin(); + PreprocessorDirectiveVectorIterator thisEnd = getDirectivesEnd(); + PreprocessorDirectiveVectorIterator otherIter = other->getDirectivesBegin(); + PreprocessorDirectiveVectorIterator 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 HeaderFile::dump(int level) { + errs() << getIndent(level) << "preprocessor directives:\n"; + PreprocessorDirectiveVectorIterator dIter = getDirectivesBegin(); + PreprocessorDirectiveVectorIterator dEnd = getDirectivesEnd(); + for (; dIter != dEnd; ++dIter) { + dIter->dump(level + 1, Name.c_str()); + } +} + +} Index: modularize/PreprocessorDirective.h =================================================================== --- modularize/PreprocessorDirective.h (revision 0) +++ modularize/PreprocessorDirective.h (revision 0) @@ -0,0 +1,87 @@ +//===--- PreprocessorDirective.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 PREPROCESSOR_DIRECTIVE_H +#define PREPROCESSOR_DIRECTIVE_H + +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceLocation.h" +#include + +namespace Modularize { + +/// \brief Stores information about one preprocessor directive. +/// +/// This class stores information about one preprocessor directive instance, +/// presently limited to #if, #elif, #ifdef, and #ifndef, since that is all +/// modularize needs for now. It stores the source file line number, a +/// directive kind code, and both the unpreprocessed and preprocessed +/// conditional source code snippet. +class PreprocessorDirective { +public: + PreprocessorDirective( + clang::tok::PPKeywordKind directiveKind, unsigned lineNumber, + std::string unconvertedConditional, std::string convertedConditional); + ~PreprocessorDirective(); + + // 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(PreprocessorDirective *other); + + // Print this directive to a stream. + void print(llvm::raw_ostream &OS, int level, std::string fileName); + + // 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); + + // Get directive spelling. + static const char *getDirectiveSpelling(clang::tok::PPKeywordKind kind); +private: + // The kind of directive. + clang::tok::PPKeywordKind 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 PreprocessorDirectiveVector; +typedef PreprocessorDirectiveVector::iterator + PreprocessorDirectiveVectorIterator; + +} // end namespace clang + +#endif Index: modularize/PreprocessorCallbacks.h =================================================================== --- modularize/PreprocessorCallbacks.h (revision 0) +++ modularize/PreprocessorCallbacks.h (revision 0) @@ -0,0 +1,97 @@ +//===--- PreprocessorCallbacks.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 PREPROCESSOR_CALLBACKS_H +#define PREPROCESSOR_CALLBACKS_H + +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceLocation.h" +#include "HeaderFile.h" +#include "MasterHeaderTracker.h" + +namespace Modularize { + +typedef llvm::StringMap SymbolMap; +typedef SymbolMap::iterator SymbolMapIterator; + +/// \brief Preprocessor callbacks for modularize. +/// +/// This class derives from the Clang PPCallbacks class to track preprocessor +/// actions, such as changing files and handling preprocessor directives and +/// macro expansions. It has to figure out when a new header file is entered +/// and left, as the provided handler is not particularly clear about it. +/// It also stores a map of macro expansions obtained from the MacroExpands +/// callback, for use by a function that effectively preprocesses a +/// conditional. It handles the top-level aspects of collecting header file +/// instance information, and tracking the preprocessor conditional directives. +class PreprocessorCallbacks : public clang::PPCallbacks { +public: + PreprocessorCallbacks(clang::Preprocessor &pp, llvm::StringRef topFile, + MasterHeaderTracker &masterHeaderTracker); + ~PreprocessorCallbacks(); + + // Overidden handlers. + void FileChanged(clang::SourceLocation Loc, FileChangeReason Reason, + clang::SrcMgr::CharacteristicKind FileType, + clang::FileID PrevFID = clang::FileID()); + void EndOfMainFile(); + void MacroExpands(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD, + clang::SourceRange Range, const clang::MacroArgs *Args); + void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange); + void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange, + clang::SourceLocation IfLoc); + void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, + const clang::MacroDirective *MD); + void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, + const clang::MacroDirective *MD); +private: + // Local accessors. + HeaderFile *getHeaderFile(const std::string &name); + void addHeaderFile(HeaderFile *headerFile); + + // Get symbol map. + SymbolMap &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(clang::SourceLocation Loc); + std::string getSourceLocationFile(clang::SourceLocation Loc); + unsigned getSourceLocationLineNumber(clang::SourceLocation Loc); + // Retrieve source snippet from file image. + std::string getSourceSnippet(clang::SourceRange sourceRange); + // Do macro substitutions in a string. + std::string getMacroSubstitutedString(std::string str); + +private: + // Local data. + MasterHeaderTracker &MasterTracker; + std::string TopFile; + clang::Preprocessor &PP; + HeaderFile *RootHeaderFile; + HeaderFile *CurrentHeaderFile; + HeaderFileMap HeadersMap; + SymbolMap Symbols; + +}; + +} // end namespace clang + +#endif Index: modularize/CMakeLists.txt =================================================================== --- modularize/CMakeLists.txt (revision 184347) +++ modularize/CMakeLists.txt (working copy) @@ -7,6 +7,13 @@ add_clang_executable(modularize Modularize.cpp + HeaderFile.cpp + HeaderInstance.cpp + HeaderTracker.cpp + MasterHeaderTracker.cpp + PreprocessorCallbacks.cpp + PreprocessorDirective.cpp + Utilities.cpp ) target_link_libraries(modularize Index: modularize/HeaderFile.h =================================================================== --- modularize/HeaderFile.h (revision 0) +++ modularize/HeaderFile.h (revision 0) @@ -0,0 +1,76 @@ +//===--- HeaderFile.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 HEADER_FILE_H +#define HEADER_FILE_H + +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/StringMap.h" +#include "PreprocessorDirective.h" +#include + +namespace Modularize { + +/// \brief Header file information. +/// +/// This class stores information about preprocessor directives +/// encountered in a header file which might be relevant to finding +/// modules-related problems. It stores a header file name and a vector +/// of PreprocessorDirective instances collected for that header file. +class HeaderFile { +public: + HeaderFile(std::string name, clang::Preprocessor &pp); + ~HeaderFile(); + + // Name accessors. + std::string &getName() { return Name; } + void setName(std::string name) { Name = name; }; + + // Preprocessor directive accessors. + PreprocessorDirectiveVector &getDirectives() { return Directives; } + int getDirectiveCount() { return Directives.size(); } + PreprocessorDirectiveVectorIterator getDirectivesBegin() + { return Directives.begin(); } + PreprocessorDirectiveVectorIterator getDirectivesEnd() + { return Directives.end(); } + PreprocessorDirective &getDirective(int index) { return Directives[index]; } + void addDirective(PreprocessorDirective &directive) + { Directives.push_back(directive); }; + + // Get highest line-numbered directive. + unsigned getLastLineNumber(); + + // Return true if this snapshot matches the other snapshot. + bool match(HeaderFile *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. + PreprocessorDirectiveVector Directives; +}; + +// Collections for users of this typs. +typedef llvm::StringMap HeaderFileMap; +typedef HeaderFileMap::iterator HeaderFileMapIterator; + +} // end namespace clang + +#endif