| //=- 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 |