//=- 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(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 &Tokens) { std::string Expanded; // Walk over the macro Tokens. typedef llvm::SmallVectorImpl::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(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 HeaderInclusionPath) : Path(HeaderInclusionPath) {} HeaderInclusionPath(const HeaderInclusionPath &Other) : Path(Other.Path) {} HeaderInclusionPath() {} std::vector 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::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 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::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 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::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 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::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 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 MacroExpansionMap; typedef std::map::iterator MacroExpansionMapIter; // Preprocessor conditional expansion item map types. typedef std::map ConditionalExpansionMap; typedef std::map::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 and 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 and 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::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::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 &Path) const { InclusionPathHandle H = 0; for (std::vector::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 &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 & getInclusionPath(InclusionPathHandle H) const { if ((H >= 0) && (H <= (InclusionPathHandle)InclusionPaths.size())) return InclusionPaths[H].Path; static std::vector 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::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::iterator IIP = MacroInfo.InclusionPathHandles.begin(), EIP = MacroInfo.InclusionPathHandles.end(); IIP != EIP; ++IIP) { const std::vector &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::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::iterator IIP = MacroInfo.InclusionPathHandles.begin(), EIP = MacroInfo.InclusionPathHandles.end(); IIP != EIP; ++IIP) { const std::vector &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 HeaderPaths; std::vector HeaderStack; std::vector 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