diff options
Diffstat (limited to 'clang-tools-extra/modularize/PreprocessorTracker.cpp')
-rw-r--r-- | clang-tools-extra/modularize/PreprocessorTracker.cpp | 1036 |
1 files changed, 1036 insertions, 0 deletions
diff --git a/clang-tools-extra/modularize/PreprocessorTracker.cpp b/clang-tools-extra/modularize/PreprocessorTracker.cpp new file mode 100644 index 00000000000..8f2f52e8d77 --- /dev/null +++ b/clang-tools-extra/modularize/PreprocessorTracker.cpp @@ -0,0 +1,1036 @@ +//=- PreprocessorTracker.cpp - Preprocessor tracking -*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------===// + +#include "clang/Lex/LexDiagnostic.h" +#include "clang/Lex/MacroArgs.h" +#include "clang/Lex/PPCallbacks.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/StringPool.h" +#include "PreprocessorTracker.h" + +namespace Modularize { + +// Forwards. +class PreprocessorTrackerImpl; + +// Some handle types + +// String handle. +typedef llvm::PooledStringPtr StringHandle; + +// Header handle. +typedef int HeaderHandle; +const HeaderHandle HeaderHandleInvalid = -1; + +// Header inclusion path handle. +typedef int InclusionPathHandle; +const InclusionPathHandle InclusionPathHandleInvalid = -1; + +// Some utility functions. + +// Get a "file:line:column" source location string. +static std::string getSourceLocationString(clang::Preprocessor &PP, + clang::SourceLocation Loc) { + if (Loc.isInvalid()) + return std::string("(none)"); + else + return Loc.printToString(PP.getSourceManager()); +} + +// Get just the file name from a source location. +static std::string getSourceLocationFile(clang::Preprocessor &PP, + clang::SourceLocation Loc) { + std::string Source(getSourceLocationString(PP, Loc)); + size_t Offset = Source.find(':', 2); + if (Offset == std::string::npos) + return Source; + return Source.substr(0, Offset); +} + +// Get just the line and column from a source location. +static void getSourceLocationLineAndColumn(clang::Preprocessor &PP, + clang::SourceLocation Loc, int &Line, + int &Column) { + clang::PresumedLoc PLoc = PP.getSourceManager().getPresumedLoc(Loc); + if (PLoc.isInvalid()) { + Line = 0; + Column = 0; + return; + } + Line = PLoc.getLine(); + Column = PLoc.getColumn(); +} + +// Retrieve source snippet from file image. +std::string getSourceString(clang::Preprocessor &PP, clang::SourceRange Range) { + clang::SourceLocation BeginLoc = Range.getBegin(); + clang::SourceLocation EndLoc = Range.getEnd(); + const char *BeginPtr = PP.getSourceManager().getCharacterData(BeginLoc); + const char *EndPtr = PP.getSourceManager().getCharacterData(EndLoc); + size_t Length = EndPtr - BeginPtr; + return llvm::StringRef(BeginPtr, Length).trim().str(); +} + +// Retrieve source line from file image. +std::string getSourceLine(clang::Preprocessor &PP, clang::SourceLocation Loc) { + const llvm::MemoryBuffer *MemBuffer = + PP.getSourceManager().getBuffer(PP.getSourceManager().getFileID(Loc)); + const char *Buffer = MemBuffer->getBufferStart(); + const char *BufferEnd = MemBuffer->getBufferEnd(); + const char *BeginPtr = PP.getSourceManager().getCharacterData(Loc); + const char *EndPtr = BeginPtr; + while (BeginPtr > Buffer) { + if (*BeginPtr == '\n') { + BeginPtr++; + break; + } + BeginPtr--; + } + while (EndPtr < BufferEnd) { + if (*EndPtr == '\n') { + break; + } + EndPtr++; + } + size_t Length = EndPtr - BeginPtr; + return llvm::StringRef(BeginPtr, Length).str(); +} + +// Get the string for the Unexpanded macro instance. +// The soureRange is expected to end at the last token +// for the macro instance, which in the case of a function-style +// macro will be a ')', but for an object-style macro, it +// will be the macro name itself. +std::string getMacroUnexpandedString(clang::SourceRange Range, + clang::Preprocessor &PP, + llvm::StringRef MacroName, + const clang::MacroInfo *MI) { + clang::SourceLocation BeginLoc(Range.getBegin()); + const char *BeginPtr = PP.getSourceManager().getCharacterData(BeginLoc); + size_t Length; + std::string Unexpanded; + if (MI->isFunctionLike()) { + clang::SourceLocation EndLoc(Range.getEnd()); + const char *EndPtr = PP.getSourceManager().getCharacterData(EndLoc) + 1; + Length = (EndPtr - BeginPtr) + 1; // +1 is ')' width. + } else + Length = MacroName.size(); + return llvm::StringRef(BeginPtr, Length).trim().str(); +} + +// Get the expansion for a macro instance, given the information +// provided by PPCallbacks. +std::string getMacroExpandedString(clang::Preprocessor &PP, + llvm::StringRef MacroName, + const clang::MacroInfo *MI, + const clang::MacroArgs *Args) { + std::string Expanded; + // Walk over the macro Tokens. + typedef clang::MacroInfo::tokens_iterator Iter; + for (Iter I = MI->tokens_begin(), E = MI->tokens_end(); I != E; ++I) { + clang::IdentifierInfo *II = I->getIdentifierInfo(); + int ArgNo = (II && Args ? MI->getArgumentNum(II) : -1); + if (ArgNo == -1) { + // This isn't an argument, just add it. + if (II == NULL) + Expanded += PP.getSpelling((*I)); // Not an identifier. + else { + // Token is for an identifier. + std::string Name = II->getName().str(); + // Check for nexted macro references. + clang::MacroInfo *MacroInfo = PP.getMacroInfo(II); + if (MacroInfo != NULL) + Expanded += getMacroExpandedString(PP, Name, MacroInfo, NULL); + else + Expanded += Name; + } + continue; + } + // We get here if it's a function-style macro with arguments. + const clang::Token *ResultArgToks; + const clang::Token *ArgTok = Args->getUnexpArgument(ArgNo); + if (Args->ArgNeedsPreexpansion(ArgTok, PP)) + ResultArgToks = &(const_cast<clang::MacroArgs *>(Args)) + ->getPreExpArgument(ArgNo, MI, PP)[0]; + else + ResultArgToks = ArgTok; // Use non-preexpanded Tokens. + // If the arg token didn't expand into anything, ignore it. + if (ResultArgToks->is(clang::tok::eof)) + continue; + unsigned NumToks = clang::MacroArgs::getArgLength(ResultArgToks); + // Append the resulting argument expansions. + for (unsigned ArgumentIndex = 0; ArgumentIndex < NumToks; ++ArgumentIndex) { + const clang::Token &AT = ResultArgToks[ArgumentIndex]; + clang::IdentifierInfo *II = AT.getIdentifierInfo(); + if (II == NULL) + Expanded += PP.getSpelling(AT); // Not an identifier. + else { + // It's an identifier. Check for further expansion. + std::string Name = II->getName().str(); + clang::MacroInfo *MacroInfo = PP.getMacroInfo(II); + if (MacroInfo != NULL) + Expanded += getMacroExpandedString(PP, Name, MacroInfo, NULL); + else + Expanded += Name; + } + } + } + return Expanded; +} + +// Get the string representing a vector of Tokens. +std::string +getTokensSpellingString(clang::Preprocessor &PP, + llvm::SmallVectorImpl<clang::Token> &Tokens) { + std::string Expanded; + // Walk over the macro Tokens. + typedef llvm::SmallVectorImpl<clang::Token>::iterator Iter; + for (Iter I = Tokens.begin(), E = Tokens.end(); I != E; ++I) + Expanded += PP.getSpelling(*I); // Not an identifier. + return llvm::StringRef(Expanded).trim().str(); +} + +// Get the expansion for a macro instance, given the information +// provided by PPCallbacks. +std::string getExpandedString(clang::Preprocessor &PP, + llvm::StringRef MacroName, + const clang::MacroInfo *MI, + const clang::MacroArgs *Args) { + std::string Expanded; + // Walk over the macro Tokens. + typedef clang::MacroInfo::tokens_iterator Iter; + for (Iter I = MI->tokens_begin(), E = MI->tokens_end(); I != E; ++I) { + clang::IdentifierInfo *II = I->getIdentifierInfo(); + int ArgNo = (II && Args ? MI->getArgumentNum(II) : -1); + if (ArgNo == -1) { + // This isn't an argument, just add it. + if (II == NULL) + Expanded += PP.getSpelling((*I)); // Not an identifier. + else { + // Token is for an identifier. + std::string Name = II->getName().str(); + // Check for nexted macro references. + clang::MacroInfo *MacroInfo = PP.getMacroInfo(II); + if (MacroInfo != NULL) + Expanded += getMacroExpandedString(PP, Name, MacroInfo, NULL); + else + Expanded += Name; + } + continue; + } + // We get here if it's a function-style macro with arguments. + const clang::Token *ResultArgToks; + const clang::Token *ArgTok = Args->getUnexpArgument(ArgNo); + if (Args->ArgNeedsPreexpansion(ArgTok, PP)) + ResultArgToks = &(const_cast<clang::MacroArgs *>(Args)) + ->getPreExpArgument(ArgNo, MI, PP)[0]; + else + ResultArgToks = ArgTok; // Use non-preexpanded Tokens. + // If the arg token didn't expand into anything, ignore it. + if (ResultArgToks->is(clang::tok::eof)) + continue; + unsigned NumToks = clang::MacroArgs::getArgLength(ResultArgToks); + // Append the resulting argument expansions. + for (unsigned ArgumentIndex = 0; ArgumentIndex < NumToks; ++ArgumentIndex) { + const clang::Token &AT = ResultArgToks[ArgumentIndex]; + clang::IdentifierInfo *II = AT.getIdentifierInfo(); + if (II == NULL) + Expanded += PP.getSpelling(AT); // Not an identifier. + else { + // It's an identifier. Check for further expansion. + std::string Name = II->getName().str(); + clang::MacroInfo *MacroInfo = PP.getMacroInfo(II); + if (MacroInfo != NULL) + Expanded += getMacroExpandedString(PP, Name, MacroInfo, NULL); + else + Expanded += Name; + } + } + } + return Expanded; +} + +// We need some operator overloads for string handles. +bool operator==(const StringHandle &H1, const StringHandle &H2) { + const char *S1 = (H1 ? *H1 : ""); + const char *S2 = (H2 ? *H2 : ""); + int Diff = strcmp(S1, S2); + return Diff == 0; +} +bool operator!=(const StringHandle &H1, const StringHandle &H2) { + const char *S1 = (H1 ? *H1 : ""); + const char *S2 = (H2 ? *H2 : ""); + int Diff = strcmp(S1, S2); + return Diff != 0; +} +bool operator<(const StringHandle &H1, const StringHandle &H2) { + const char *S1 = (H1 ? *H1 : ""); + const char *S2 = (H2 ? *H2 : ""); + int Diff = strcmp(S1, S2); + return Diff < 0; +} +bool operator>(const StringHandle &H1, const StringHandle &H2) { + const char *S1 = (H1 ? *H1 : ""); + const char *S2 = (H2 ? *H2 : ""); + int Diff = strcmp(S1, S2); + return Diff > 0; +} + +// Preprocessor item key. +// +// This class represents a location in a source file, for use +// as a key representing a unique name/file/line/column quadruplet, +// which in this case is used to identify a macro expansion instance, +// but could be used for other things as well. +// The file is a header file handle, the line is a line number, +// and the column is a column number. +class PPItemKey { +public: + PPItemKey(clang::Preprocessor &PP, StringHandle Name, HeaderHandle File, + clang::SourceLocation Loc) + : Name(Name), File(File) { + getSourceLocationLineAndColumn(PP, Loc, Line, Column); + } + PPItemKey(StringHandle Name, HeaderHandle File, int Line, int Column) + : Name(Name), File(File), Line(Line), Column(Column) {} + PPItemKey(const PPItemKey &Other) + : Name(Other.Name), File(Other.File), Line(Other.Line), + Column(Other.Column) {} + PPItemKey() : File(HeaderHandleInvalid), Line(0), Column(0) {} + bool operator==(const PPItemKey &Other) const { + if (Name != Other.Name) + return false; + if (File != Other.File) + return false; + if (Line != Other.Line) + return false; + return Column == Other.Column; + } + bool operator<(const PPItemKey &Other) const { + if (Name < Other.Name) + return true; + else if (Name > Other.Name) + return false; + if (File < Other.File) + return true; + else if (File > Other.File) + return false; + if (Line < Other.Line) + return true; + else if (Line > Other.Line) + return false; + return Column < Other.Column; + } + StringHandle Name; + HeaderHandle File; + int Line; + int Column; +}; + +// Header inclusion path. +class HeaderInclusionPath { +public: + HeaderInclusionPath(std::vector<HeaderHandle> HeaderInclusionPath) + : Path(HeaderInclusionPath) {} + HeaderInclusionPath(const HeaderInclusionPath &Other) : Path(Other.Path) {} + HeaderInclusionPath() {} + std::vector<HeaderHandle> Path; +}; + +// Macro expansion instance. +// +// This class represents an instance of a macro expansion with a +// unique value. It also stores the unique header inclusion paths +// for use in telling the user the nested include path f +class MacroExpansionInstance { +public: + MacroExpansionInstance(StringHandle MacroExpanded, + PPItemKey &DefinitionLocation, + StringHandle DefinitionSourceLine, + InclusionPathHandle H) + : MacroExpanded(MacroExpanded), DefinitionLocation(DefinitionLocation), + DefinitionSourceLine(DefinitionSourceLine) { + InclusionPathHandles.push_back(H); + } + MacroExpansionInstance() {} + + // Check for the presence of a header inclusion path handle entry. + // Return false if not found. + bool haveInclusionPathHandle(InclusionPathHandle H) { + for (std::vector<InclusionPathHandle>::iterator + I = InclusionPathHandles.begin(), + E = InclusionPathHandles.end(); + I != E; ++I) { + if (*I == H) + return true; + } + return InclusionPathHandleInvalid; + } + // Add a new header inclusion path entry, if not already present. + void addInclusionPathHandle(InclusionPathHandle H) { + if (!haveInclusionPathHandle(H)) + InclusionPathHandles.push_back(H); + } + + // A string representing the macro instance after preprocessing. + StringHandle MacroExpanded; + // A file/line/column triplet representing the macro definition location. + PPItemKey DefinitionLocation; + // A place to save the macro definition line string. + StringHandle DefinitionSourceLine; + // The header inclusion path handles for all the instances. + std::vector<InclusionPathHandle> InclusionPathHandles; +}; + +// Macro expansion instance tracker. +// +// This class represents one macro expansion, keyed by a PPItemKey. +// It stores a string representing the macro reference in the source, +// and a list of ConditionalExpansionInstances objects representing +// the unique value the condition expands to in instances of the header. +class MacroExpansionTracker { +public: + MacroExpansionTracker(StringHandle MacroUnexpanded, + StringHandle MacroExpanded, + StringHandle InstanceSourceLine, + PPItemKey &DefinitionLocation, + StringHandle DefinitionSourceLine, + InclusionPathHandle InclusionPathHandle) + : MacroUnexpanded(MacroUnexpanded), + InstanceSourceLine(InstanceSourceLine) { + addMacroExpansionInstance(MacroExpanded, DefinitionLocation, + DefinitionSourceLine, InclusionPathHandle); + } + MacroExpansionTracker() {} + + // Find a matching macro expansion instance. + MacroExpansionInstance * + findMacroExpansionInstance(StringHandle MacroExpanded, + PPItemKey &DefinitionLocation) { + for (std::vector<MacroExpansionInstance>::iterator + I = MacroExpansionInstances.begin(), + E = MacroExpansionInstances.end(); + I != E; ++I) { + if ((I->MacroExpanded == MacroExpanded) && + (I->DefinitionLocation == DefinitionLocation)) { + return &*I; // Found. + } + } + return NULL; // Not found. + } + + // Add a macro expansion instance. + void addMacroExpansionInstance(StringHandle MacroExpanded, + PPItemKey &DefinitionLocation, + StringHandle DefinitionSourceLine, + InclusionPathHandle InclusionPathHandle) { + MacroExpansionInstances.push_back( + MacroExpansionInstance(MacroExpanded, DefinitionLocation, + DefinitionSourceLine, InclusionPathHandle)); + } + + // Return true if there is a mismatch. + bool hasMismatch() { return MacroExpansionInstances.size() > 1; } + + // A string representing the macro instance without expansion. + StringHandle MacroUnexpanded; + // A place to save the macro instance source line string. + StringHandle InstanceSourceLine; + // The macro expansion instances. + // If all instances of the macro expansion expand to the same value, + // This vector will only have one instance. + std::vector<MacroExpansionInstance> MacroExpansionInstances; +}; + +// Conditional expansion instance. +// +// This class represents an instance of a macro expansion with a +// unique value. It also stores the unique header inclusion paths +// for use in telling the user the nested include path f +class ConditionalExpansionInstance { +public: + ConditionalExpansionInstance(bool ConditionValue, InclusionPathHandle H) + : ConditionValue(ConditionValue) { + InclusionPathHandles.push_back(H); + } + ConditionalExpansionInstance() {} + + // Check for the presence of a header inclusion path handle entry. + // Return false if not found. + bool haveInclusionPathHandle(InclusionPathHandle H) { + for (std::vector<InclusionPathHandle>::iterator + I = InclusionPathHandles.begin(), + E = InclusionPathHandles.end(); + I != E; ++I) { + if (*I == H) + return true; + } + return InclusionPathHandleInvalid; + } + // Add a new header inclusion path entry, if not already present. + void addInclusionPathHandle(InclusionPathHandle H) { + if (!haveInclusionPathHandle(H)) + InclusionPathHandles.push_back(H); + } + + // A flag representing the evaluated condition value. + bool ConditionValue; + // The header inclusion path handles for all the instances. + std::vector<InclusionPathHandle> InclusionPathHandles; +}; + +// Conditional directive instance tracker. +// +// This class represents one conditional directive, keyed by a PPItemKey. +// It stores a string representing the macro reference in the source, +// and a list of MacroExpansionInstance objects representing +// the unique value the macro expands to in instances of the header. +class ConditionalTracker { +public: + ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind, + bool ConditionValue, StringHandle ConditionUnexpanded, + InclusionPathHandle InclusionPathHandle) + : DirectiveKind(DirectiveKind), ConditionUnexpanded(ConditionUnexpanded) { + addConditionalExpansionInstance(ConditionValue, InclusionPathHandle); + } + ConditionalTracker() {} + + // Find a matching condition expansion instance. + ConditionalExpansionInstance * + findConditionalExpansionInstance(bool ConditionValue) { + for (std::vector<ConditionalExpansionInstance>::iterator + I = ConditionalExpansionInstances.begin(), + E = ConditionalExpansionInstances.end(); + I != E; ++I) { + if (I->ConditionValue == ConditionValue) { + return &*I; // Found. + } + } + return NULL; // Not found. + } + + // Add a conditional expansion instance. + void + addConditionalExpansionInstance(bool ConditionValue, + InclusionPathHandle InclusionPathHandle) { + ConditionalExpansionInstances.push_back( + ConditionalExpansionInstance(ConditionValue, InclusionPathHandle)); + } + + // Return true if there is a mismatch. + bool hasMismatch() { return ConditionalExpansionInstances.size() > 1; } + + // The kind of directive. + clang::tok::PPKeywordKind DirectiveKind; + // A string representing the macro instance without expansion. + StringHandle ConditionUnexpanded; + // The condition expansion instances. + // If all instances of the conditional expression expand to the same value, + // This vector will only have one instance. + std::vector<ConditionalExpansionInstance> ConditionalExpansionInstances; +}; + +// 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. +class PreprocessorCallbacks : public clang::PPCallbacks { +public: + PreprocessorCallbacks(PreprocessorTrackerImpl &ppTracker, + clang::Preprocessor &PP, llvm::StringRef rootHeaderFile) + : PPTracker(ppTracker), PP(PP), RootHeaderFile(rootHeaderFile) {} + ~PreprocessorCallbacks() {} + + // Overidden handlers. + void FileChanged(clang::SourceLocation Loc, + clang::PPCallbacks::FileChangeReason Reason, + clang::SrcMgr::CharacteristicKind FileType, + clang::FileID PrevFID = clang::FileID()); + void MacroExpands(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD, clang::SourceRange Range, + const clang::MacroArgs *Args); + void Defined(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD, clang::SourceRange Range); + void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange, + bool ConditionResult); + void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange, + bool ConditionResult, 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: + PreprocessorTrackerImpl &PPTracker; + clang::Preprocessor &PP; + std::string RootHeaderFile; +}; + +// Preprocessor macro expansion item map types. +typedef std::map<PPItemKey, MacroExpansionTracker> MacroExpansionMap; +typedef std::map<PPItemKey, MacroExpansionTracker>::iterator +MacroExpansionMapIter; + +// Preprocessor conditional expansion item map types. +typedef std::map<PPItemKey, ConditionalTracker> ConditionalExpansionMap; +typedef std::map<PPItemKey, ConditionalTracker>::iterator +ConditionalExpansionMapIter; + +// Preprocessor tracker for modularize. +// +// This class stores information about all the headers processed in the +// course of running modularize. +class PreprocessorTrackerImpl : public PreprocessorTracker { +public: + PreprocessorTrackerImpl() + : CurrentInclusionPathHandle(InclusionPathHandleInvalid) {} + ~PreprocessorTrackerImpl() {} + + // Handle entering a preprocessing session. + void handlePreprocessorEntry(clang::Preprocessor &PP, + llvm::StringRef rootHeaderFile) { + assert((HeaderStack.size() == 0) && "Header stack should be empty."); + pushHeaderHandle(addHeader(rootHeaderFile)); + PP.addPPCallbacks(new PreprocessorCallbacks(*this, PP, rootHeaderFile)); + } + // Handle exiting a preprocessing session. + void handlePreprocessorExit() { HeaderStack.clear(); } + + // Handle entering a header source file. + void handleHeaderEntry(clang::Preprocessor &PP, llvm::StringRef HeaderPath) { + // Ignore <built-in> and <command-line> to reduce message clutter. + if (HeaderPath.startswith("<")) + return; + HeaderHandle H = addHeader(HeaderPath); + if (H != getCurrentHeaderHandle()) + pushHeaderHandle(H); + } + // Handle exiting a header source file. + void handleHeaderExit(llvm::StringRef HeaderPath) { + // Ignore <built-in> and <command-line> to reduce message clutter. + if (HeaderPath.startswith("<")) + return; + HeaderHandle H = findHeaderHandle(HeaderPath); + if (isHeaderHandleInStack(H)) { + while ((H != getCurrentHeaderHandle()) && (HeaderStack.size() != 0)) + popHeaderHandle(); + } + } + + // Lookup/add string. + StringHandle addString(llvm::StringRef Str) { return Strings.intern(Str); } + + // Get the handle of a header file entry. + // Return HeaderHandleInvalid if not found. + HeaderHandle findHeaderHandle(llvm::StringRef HeaderPath) const { + HeaderHandle H = 0; + for (std::vector<StringHandle>::const_iterator I = HeaderPaths.begin(), + E = HeaderPaths.end(); + I != E; ++I, ++H) { + if (**I == HeaderPath) + return H; + } + return HeaderHandleInvalid; + } + + // Add a new header file entry, or return existing handle. + // Return the header handle. + HeaderHandle addHeader(llvm::StringRef HeaderPath) { + std::string canonicalPath(HeaderPath); + std::replace(canonicalPath.begin(), canonicalPath.end(), '\\', '/'); + HeaderHandle H = findHeaderHandle(canonicalPath); + if (H == HeaderHandleInvalid) { + H = HeaderPaths.size(); + HeaderPaths.push_back(addString(canonicalPath)); + } + return H; + } + + // Return a header file path string given its handle. + StringHandle getHeaderFilePath(HeaderHandle H) const { + if ((H >= 0) && (H < (HeaderHandle)HeaderPaths.size())) + return HeaderPaths[H]; + return StringHandle(); + } + + // Returns a handle to the inclusion path. + InclusionPathHandle pushHeaderHandle(HeaderHandle H) { + HeaderStack.push_back(H); + return CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack); + } + // Pops the last header handle from the stack; + void popHeaderHandle() { + // assert((HeaderStack.size() != 0) && "Header stack already empty."); + if (HeaderStack.size() != 0) { + HeaderStack.pop_back(); + CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack); + } + } + // Get the top handle on the header stack. + HeaderHandle getCurrentHeaderHandle() const { + if (HeaderStack.size() != 0) + return HeaderStack.back(); + return HeaderHandleInvalid; + } + + // Check for presence of header handle in the header stack. + bool isHeaderHandleInStack(HeaderHandle H) const { + for (std::vector<HeaderHandle>::const_iterator I = HeaderStack.begin(), + E = HeaderStack.end(); + I != E; ++I) { + if (*I == H) + return true; + } + return false; + } + + // Get the handle of a header inclusion path entry. + // Return InclusionPathHandleInvalid if not found. + InclusionPathHandle + findInclusionPathHandle(const std::vector<HeaderHandle> &Path) const { + InclusionPathHandle H = 0; + for (std::vector<HeaderInclusionPath>::const_iterator + I = InclusionPaths.begin(), + E = InclusionPaths.end(); + I != E; ++I, ++H) { + if (I->Path == Path) + return H; + } + return HeaderHandleInvalid; + } + // Add a new header inclusion path entry, or return existing handle. + // Return the header inclusion path entry handle. + InclusionPathHandle + addInclusionPathHandle(const std::vector<HeaderHandle> &Path) { + InclusionPathHandle H = findInclusionPathHandle(Path); + if (H == HeaderHandleInvalid) { + H = InclusionPaths.size(); + InclusionPaths.push_back(HeaderInclusionPath(Path)); + } + return H; + } + // Return the current inclusion path handle. + InclusionPathHandle getCurrentInclusionPathHandle() const { + return CurrentInclusionPathHandle; + } + + // Return an inclusion path given its handle. + const std::vector<HeaderHandle> & + getInclusionPath(InclusionPathHandle H) const { + if ((H >= 0) && (H <= (InclusionPathHandle)InclusionPaths.size())) + return InclusionPaths[H].Path; + static std::vector<HeaderHandle> Empty; + return Empty; + } + + // Add a macro expansion instance. + void addMacroExpansionInstance(clang::Preprocessor &PP, HeaderHandle H, + clang::SourceLocation InstanceLoc, + clang::SourceLocation DefinitionLoc, + clang::IdentifierInfo *II, + llvm::StringRef MacroUnexpanded, + llvm::StringRef MacroExpanded, + InclusionPathHandle InclusionPathHandle) { + StringHandle MacroName = addString(II->getName()); + PPItemKey instanceKey(PP, MacroName, H, InstanceLoc); + PPItemKey definitionKey(PP, MacroName, H, DefinitionLoc); + MacroExpansionMapIter I = MacroExpansions.find(instanceKey); + if (I == MacroExpansions.end()) { + std::string instanceSourceLine = + getSourceLocationString(PP, InstanceLoc) + ":\n" + + getSourceLine(PP, InstanceLoc) + "\n"; + std::string definitionSourceLine = + getSourceLocationString(PP, DefinitionLoc) + ":\n" + + getSourceLine(PP, DefinitionLoc) + "\n"; + MacroExpansions[instanceKey] = MacroExpansionTracker( + addString(MacroUnexpanded), addString(MacroExpanded), + addString(instanceSourceLine), definitionKey, + addString(definitionSourceLine), InclusionPathHandle); + } else { + MacroExpansionTracker &CondTracker = I->second; + MacroExpansionInstance *MacroInfo = + CondTracker.findMacroExpansionInstance(addString(MacroExpanded), + definitionKey); + if (MacroInfo != NULL) + MacroInfo->addInclusionPathHandle(InclusionPathHandle); + else { + std::string definitionSourceLine = + getSourceLocationString(PP, DefinitionLoc) + ":\n" + + getSourceLine(PP, DefinitionLoc) + "\n"; + CondTracker.addMacroExpansionInstance( + addString(MacroExpanded), definitionKey, + addString(definitionSourceLine), InclusionPathHandle); + } + } + } + + // Add a conditional expansion instance. + void + addConditionalExpansionInstance(clang::Preprocessor &PP, HeaderHandle H, + clang::SourceLocation InstanceLoc, + clang::tok::PPKeywordKind DirectiveKind, + bool ConditionValue, + llvm::StringRef ConditionUnexpanded, + InclusionPathHandle InclusionPathHandle) { + StringHandle conditionUnexpanded(addString(ConditionUnexpanded)); + PPItemKey instanceKey(PP, conditionUnexpanded, H, InstanceLoc); + ConditionalExpansionMapIter I = ConditionalExpansions.find(instanceKey); + if (I == ConditionalExpansions.end()) { + std::string instanceSourceLine = + getSourceLocationString(PP, InstanceLoc) + ":\n" + + getSourceLine(PP, InstanceLoc) + "\n"; + ConditionalExpansions[instanceKey] = + ConditionalTracker(DirectiveKind, ConditionValue, conditionUnexpanded, + InclusionPathHandle); + } else { + ConditionalTracker &CondTracker = I->second; + ConditionalExpansionInstance *MacroInfo = + CondTracker.findConditionalExpansionInstance(ConditionValue); + if (MacroInfo != NULL) + MacroInfo->addInclusionPathHandle(InclusionPathHandle); + else { + CondTracker.addConditionalExpansionInstance(ConditionValue, + InclusionPathHandle); + } + } + } + + // Report on inconsistent macro instances. + // Returns true if any mismatches. + bool reportInconsistentMacros(llvm::raw_ostream &OS) { + bool returnValue = false; + for (MacroExpansionMapIter I = MacroExpansions.begin(), + E = MacroExpansions.end(); + I != E; ++I) { + const PPItemKey &ItemKey = I->first; + MacroExpansionTracker &MacroExpTracker = I->second; + if (!MacroExpTracker.hasMismatch()) + continue; + returnValue = true; + OS << *MacroExpTracker.InstanceSourceLine; + if (ItemKey.Column > 0) + OS << std::string(ItemKey.Column - 1, ' ') << "^\n"; + OS << "error: Macro instance '" << *MacroExpTracker.MacroUnexpanded + << "' has different values in this header, depending on how it was " + "included.\n"; + for (std::vector<MacroExpansionInstance>::iterator + IMT = MacroExpTracker.MacroExpansionInstances.begin(), + EMT = MacroExpTracker.MacroExpansionInstances.end(); + IMT != EMT; ++IMT) { + MacroExpansionInstance &MacroInfo = *IMT; + OS << " '" << *MacroExpTracker.MacroUnexpanded << "' Expanded to: '" + << *MacroInfo.MacroExpanded + << "' with respect to these inclusion paths:\n"; + for (std::vector<InclusionPathHandle>::iterator + IIP = MacroInfo.InclusionPathHandles.begin(), + EIP = MacroInfo.InclusionPathHandles.end(); + IIP != EIP; ++IIP) { + const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP); + int Count = (int)ip.size(); + for (int Index = 0; Index < Count; ++Index) { + HeaderHandle H = ip[Index]; + OS << std::string((Index * 2) + 4, ' ') << *getHeaderFilePath(H) + << "\n"; + } + } + // For a macro that wasn't defined, we flag it by using the + // instance location. + // If there is a definition... + if (MacroInfo.DefinitionLocation.Line != ItemKey.Line) { + OS << *MacroInfo.DefinitionSourceLine; + if (MacroInfo.DefinitionLocation.Column > 0) + OS << std::string(MacroInfo.DefinitionLocation.Column - 1, ' ') + << "^\n"; + OS << "Macro defined here.\n"; + } else + OS << "(no macro definition)" + << "\n"; + } + } + return returnValue; + } + + // Report on inconsistent conditional instances. + // Returns true if any mismatches. + bool reportInconsistentConditionals(llvm::raw_ostream &OS) { + bool returnValue = false; + for (ConditionalExpansionMapIter I = ConditionalExpansions.begin(), + E = ConditionalExpansions.end(); + I != E; ++I) { + const PPItemKey &ItemKey = I->first; + ConditionalTracker &CondTracker = I->second; + if (!CondTracker.hasMismatch()) + continue; + returnValue = true; + OS << *HeaderPaths[ItemKey.File] << ":" << ItemKey.Line << ":" + << ItemKey.Column << "\n"; + OS << "#" << getDirectiveSpelling(CondTracker.DirectiveKind) << " " + << *CondTracker.ConditionUnexpanded << "\n"; + OS << "^\n"; + OS << "error: Conditional expression instance '" + << *CondTracker.ConditionUnexpanded + << "' has different values in this header, depending on how it was " + "included.\n"; + for (std::vector<ConditionalExpansionInstance>::iterator + IMT = CondTracker.ConditionalExpansionInstances.begin(), + EMT = CondTracker.ConditionalExpansionInstances.end(); + IMT != EMT; ++IMT) { + ConditionalExpansionInstance &MacroInfo = *IMT; + OS << " '" << *CondTracker.ConditionUnexpanded << "' Expanded to: '" + << (MacroInfo.ConditionValue ? "true" : "false") + << "' with respect to these inclusion paths:\n"; + for (std::vector<InclusionPathHandle>::iterator + IIP = MacroInfo.InclusionPathHandles.begin(), + EIP = MacroInfo.InclusionPathHandles.end(); + IIP != EIP; ++IIP) { + const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP); + int Count = (int)ip.size(); + for (int Index = 0; Index < Count; ++Index) { + HeaderHandle H = ip[Index]; + OS << std::string((Index * 2) + 4, ' ') << *getHeaderFilePath(H) + << "\n"; + } + } + } + } + return returnValue; + } + + // Get directive spelling. + static const char *getDirectiveSpelling(clang::tok::PPKeywordKind kind) { + switch (kind) { + case clang::tok::pp_if: + return "if"; + case clang::tok::pp_elif: + return "elif"; + case clang::tok::pp_ifdef: + return "ifdef"; + case clang::tok::pp_ifndef: + return "ifndef"; + default: + return "(unknown)"; + } + } + +private: + llvm::StringPool Strings; + std::vector<StringHandle> HeaderPaths; + std::vector<HeaderHandle> HeaderStack; + std::vector<HeaderInclusionPath> InclusionPaths; + InclusionPathHandle CurrentInclusionPathHandle; + MacroExpansionMap MacroExpansions; + ConditionalExpansionMap ConditionalExpansions; +}; + +// PreprocessorTracker functions. + +// PreprocessorTracker desctructor. +PreprocessorTracker::~PreprocessorTracker() {} + +// Create instance of PreprocessorTracker. +PreprocessorTracker *PreprocessorTracker::create() { + return new PreprocessorTrackerImpl(); +} + +// Preprocessor callbacks for modularize. + +// Handle file entry/exit. +void PreprocessorCallbacks::FileChanged( + clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason, + clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) { + switch (Reason) { + case EnterFile: + PPTracker.handleHeaderEntry(PP, getSourceLocationFile(PP, Loc)); + break; + case ExitFile: + if (PrevFID.isInvalid()) + PPTracker.handleHeaderExit(RootHeaderFile); + else + PPTracker.handleHeaderExit(getSourceLocationFile(PP, Loc)); + break; + case SystemHeaderPragma: + return; + case RenameFile: + return; + default: + return; + } +} + +// Handle macro expansion. +void PreprocessorCallbacks::MacroExpands(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD, + clang::SourceRange Range, + const clang::MacroArgs *Args) { + clang::SourceLocation Loc = Range.getBegin(); + clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo(); + const clang::MacroInfo *MI = PP.getMacroInfo(II); + std::string MacroName = II->getName().str(); + std::string Unexpanded(getMacroUnexpandedString(Range, PP, MacroName, MI)); + std::string Expanded(getMacroExpandedString(PP, MacroName, MI, Args)); + PPTracker.addMacroExpansionInstance( + PP, PPTracker.getCurrentHeaderHandle(), Loc, MI->getDefinitionLoc(), II, + Unexpanded, Expanded, PPTracker.getCurrentInclusionPathHandle()); +} + +void PreprocessorCallbacks::Defined(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD, + clang::SourceRange Range) { + clang::SourceLocation Loc(Range.getBegin()); + clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo(); + const clang::MacroInfo *MI = PP.getMacroInfo(II); + std::string MacroName = II->getName().str(); + std::string Unexpanded(getSourceString(PP, Range)); + PPTracker.addMacroExpansionInstance( + PP, PPTracker.getCurrentHeaderHandle(), Loc, + (MI ? MI->getDefinitionLoc() : Loc), II, Unexpanded, + (MI ? "true" : "false"), PPTracker.getCurrentInclusionPathHandle()); +} + +void PreprocessorCallbacks::If(clang::SourceLocation Loc, + clang::SourceRange ConditionRange, + bool ConditionResult) { + std::string Unexpanded(getSourceString(PP, ConditionRange)); + PPTracker.addConditionalExpansionInstance( + PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_if, + ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle()); +} + +void PreprocessorCallbacks::Elif(clang::SourceLocation Loc, + clang::SourceRange ConditionRange, + bool ConditionResult, + clang::SourceLocation IfLoc) { + std::string Unexpanded(getSourceString(PP, ConditionRange)); + PPTracker.addConditionalExpansionInstance( + PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_elif, + ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle()); +} + +void PreprocessorCallbacks::Ifdef(clang::SourceLocation Loc, + const clang::Token &MacroNameTok, + const clang::MacroDirective *MD) { + bool IsDefined = (MD != 0); + PPTracker.addConditionalExpansionInstance( + PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifdef, + IsDefined, PP.getSpelling(MacroNameTok), + PPTracker.getCurrentInclusionPathHandle()); +} + +void PreprocessorCallbacks::Ifndef(clang::SourceLocation Loc, + const clang::Token &MacroNameTok, + const clang::MacroDirective *MD) { + bool IsNotDefined = (MD == 0); + PPTracker.addConditionalExpansionInstance( + PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifndef, + IsNotDefined, PP.getSpelling(MacroNameTok), + PPTracker.getCurrentInclusionPathHandle()); +} +} // end namespace Modularize |