//===--- PreprocessorTracker.cpp - Preprocessor tracking -*- C++ -*------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===--------------------------------------------------------------------===// // // The Basic Idea (Macro and Conditional Checking) // // Basically we install a PPCallbacks-derived object to track preprocessor // activity, namely when a header file is entered/exited, when a macro // is expanded, when "defined" is used, and when #if, #elif, #ifdef, // and #ifndef are used. We save the state of macro and "defined" // expressions in a map, keyed on a name/file/line/column quadruple. // The map entries store the different states (values) that a macro expansion, // "defined" expression, or condition expression has in the course of // processing for the one location in the one header containing it, // plus a list of the nested include stacks for the states. When a macro // or "defined" expression evaluates to the same value, which is the // desired case, only one state is stored. Similarly, for conditional // directives, we save the condition expression states in a separate map. // // This information is collected as modularize compiles all the headers // given to it to process. After all the compilations are performed, // a check is performed for any entries in the maps that contain more // than one different state, and for these an output message is generated. // // For example: // // (...)/SubHeader.h:11:5: // #if SYMBOL == 1 // ^ // error: Macro instance 'SYMBOL' has different values in this header, // depending on how it was included. // 'SYMBOL' expanded to: '1' with respect to these inclusion paths: // (...)/Header1.h // (...)/SubHeader.h // (...)/SubHeader.h:3:9: // #define SYMBOL 1 // ^ // Macro defined here. // 'SYMBOL' expanded to: '2' with respect to these inclusion paths: // (...)/Header2.h // (...)/SubHeader.h // (...)/SubHeader.h:7:9: // #define SYMBOL 2 // ^ // Macro defined here. // // The Basic Idea ('Extern "C/C++" {}' Or 'namespace {}') With Nested // '#include' Checking) // // To check for '#include' directives nested inside 'Extern "C/C++" {}' // or 'namespace {}' blocks, we keep track of the '#include' directives // while running the preprocessor, and later during a walk of the AST // we call a function to check for any '#include' directives inside // an 'Extern "C/C++" {}' or 'namespace {}' block, given its source // range. // // Design and Implementation Details (Macro and Conditional Checking) // // A PreprocessorTrackerImpl class implements the PreprocessorTracker // interface. It uses a PreprocessorCallbacks class derived from PPCallbacks // to track preprocessor activity, namely entering/exiting a header, macro // expansions, use of "defined" expressions, and #if, #elif, #ifdef, and // #ifndef conditional directives. PreprocessorTrackerImpl stores a map // of MacroExpansionTracker objects keyed on a name/file/line/column // value represented by a light-weight PPItemKey value object. This // is the key top-level data structure tracking the values of macro // expansion instances. Similarly, it stores a map of ConditionalTracker // objects with the same kind of key, for tracking preprocessor conditional // directives. // // The MacroExpansionTracker object represents one macro reference or use // of a "defined" expression in a header file. It stores a handle to a // string representing the unexpanded macro instance, a handle to a string // representing the unpreprocessed source line containing the unexpanded // macro instance, and a vector of one or more MacroExpansionInstance // objects. // // The MacroExpansionInstance object represents one or more expansions // of a macro reference, for the case where the macro expands to the same // value. MacroExpansionInstance stores a handle to a string representing // the expanded macro value, a PPItemKey representing the file/line/column // where the macro was defined, a handle to a string representing the source // line containing the macro definition, and a vector of InclusionPathHandle // values that represents the hierarchies of include files for each case // where the particular header containing the macro reference was referenced // or included. // In the normal case where a macro instance always expands to the same // value, the MacroExpansionTracker object will only contain one // MacroExpansionInstance representing all the macro expansion instances. // If a case was encountered where a macro instance expands to a value // that is different from that seen before, or the macro was defined in // a different place, a new MacroExpansionInstance object representing // that case will be added to the vector in MacroExpansionTracker. If a // macro instance expands to a value already seen before, the // InclusionPathHandle representing that case's include file hierarchy // will be added to the existing MacroExpansionInstance object. // For checking conditional directives, the ConditionalTracker class // functions similarly to MacroExpansionTracker, but tracks an #if, // #elif, #ifdef, or #ifndef directive in a header file. It stores // a vector of one or two ConditionalExpansionInstance objects, // representing the cases where the conditional expression evaluates // to true or false. This latter object stores the evaluated value // of the condition expression (a bool) and a vector of // InclusionPathHandles. // // To reduce the instances of string and object copying, the // PreprocessorTrackerImpl class uses a StringPool to save all stored // strings, and defines a StringHandle type to abstract the references // to the strings. // // PreprocessorTrackerImpl also maintains a list representing the unique // headers, which is just a vector of StringHandle's for the header file // paths. A HeaderHandle abstracts a reference to a header, and is simply // the index of the stored header file path. // // A HeaderInclusionPath class abstracts a unique hierarchy of header file // inclusions. It simply stores a vector of HeaderHandles ordered from the // top-most header (the one from the header list passed to modularize) down // to the header containing the macro reference. PreprocessorTrackerImpl // stores a vector of these objects. An InclusionPathHandle typedef // abstracts a reference to one of the HeaderInclusionPath objects, and is // simply the index of the stored HeaderInclusionPath object. The // MacroExpansionInstance object stores a vector of these handles so that // the reporting function can display the include hierarchies for the macro // expansion instances represented by that object, to help the user // understand how the header was included. (A future enhancement might // be to associate a line number for the #include directives, but I // think not doing so is good enough for the present.) // // A key reason for using these opaque handles was to try to keep all the // internal objects light-weight value objects, in order to reduce string // and object copying overhead, and to abstract this implementation detail. // // The key data structures are built up while modularize runs the headers // through the compilation. A PreprocessorTracker instance is created and // passed down to the AST action and consumer objects in modularize. For // each new compilation instance, the consumer calls the // PreprocessorTracker's handleNewPreprocessorEntry function, which sets // up a PreprocessorCallbacks object for the preprocessor. At the end of // the compilation instance, the PreprocessorTracker's // handleNewPreprocessorExit function handles cleaning up with respect // to the preprocessing instance. // // The PreprocessorCallbacks object uses an overridden FileChanged callback // to determine when a header is entered and exited (including exiting the // header during #include directives). It calls PreprocessorTracker's // handleHeaderEntry and handleHeaderExit functions upon entering and // exiting a header. These functions manage a stack of header handles // representing by a vector, pushing and popping header handles as headers // are entered and exited. When a HeaderInclusionPath object is created, // it simply copies this stack. // // The PreprocessorCallbacks object uses an overridden MacroExpands callback // to track when a macro expansion is performed. It calls a couple of helper // functions to get the unexpanded and expanded macro values as strings, but // then calls PreprocessorTrackerImpl's addMacroExpansionInstance function to // do the rest of the work. The getMacroExpandedString function uses the // preprocessor's getSpelling to convert tokens to strings using the // information passed to the MacroExpands callback, and simply concatenates // them. It makes recursive calls to itself to handle nested macro // definitions, and also handles function-style macros. // // PreprocessorTrackerImpl's addMacroExpansionInstance function looks for // an existing MacroExpansionTracker entry in its map of MacroExampleTracker // objects. If none exists, it adds one with one MacroExpansionInstance and // returns. If a MacroExpansionTracker object already exists, it looks for // an existing MacroExpansionInstance object stored in the // MacroExpansionTracker object, one that matches the macro expanded value // and the macro definition location. If a matching MacroExpansionInstance // object is found, it just adds the current HeaderInclusionPath object to // it. If not found, it creates and stores a new MacroExpansionInstance // object. The addMacroExpansionInstance function calls a couple of helper // functions to get the pre-formatted location and source line strings for // the macro reference and the macro definition stored as string handles. // These helper functions use the current source manager from the // preprocessor. This is done in advance at this point in time because the // source manager doesn't exist at the time of the reporting. // // For conditional check, the PreprocessorCallbacks class overrides the // PPCallbacks handlers for #if, #elif, #ifdef, and #ifndef. These handlers // call the addConditionalExpansionInstance method of // PreprocessorTrackerImpl. The process is similar to that of macros, but // with some different data and error messages. A lookup is performed for // the conditional, and if a ConditionalTracker object doesn't yet exist for // the conditional, a new one is added, including adding a // ConditionalExpansionInstance object to it to represent the condition // expression state. If a ConditionalTracker for the conditional does // exist, a lookup is made for a ConditionalExpansionInstance object // matching the condition expression state. If one exists, a // HeaderInclusionPath is added to it. Otherwise a new // ConditionalExpansionInstance entry is made. If a ConditionalTracker // has two ConditionalExpansionInstance objects, it means there was a // conflict, meaning the conditional expression evaluated differently in // one or more cases. // // After modularize has performed all the compilations, it enters a phase // of error reporting. This new feature adds to this reporting phase calls // to the PreprocessorTracker's reportInconsistentMacros and // reportInconsistentConditionals functions. These functions walk the maps // of MacroExpansionTracker's and ConditionalTracker's respectively. If // any of these objects have more than one MacroExpansionInstance or // ConditionalExpansionInstance objects, it formats and outputs an error // message like the example shown previously, using the stored data. // // A potential issue is that there is some overlap between the #if/#elif // conditional and macro reporting. I could disable the #if and #elif, // leaving just the #ifdef and #ifndef, since these don't overlap. Or, // to make clearer the separate reporting phases, I could add an output // message marking the phases. // // Design and Implementation Details ('Extern "C/C++" {}' Or // 'namespace {}') With Nested '#include' Checking) // // We override the InclusionDirective in PPCallbacks to record information // about each '#include' directive encountered during preprocessing. // We co-opt the PPItemKey class to store the information about each // '#include' directive, including the source file name containing the // directive, the name of the file being included, and the source line // and column of the directive. We store these object in a vector, // after first check to see if an entry already exists. // // Later, while the AST is being walked for other checks, we provide // visit handlers for 'extern "C/C++" {}' and 'namespace (name) {}' // blocks, checking to see if any '#include' directives occurred // within the blocks, reporting errors if any found. // // Future Directions // // We probably should add options to disable any of the checks, in case // there is some problem with them, or the messages get too verbose. // // With the map of all the macro and conditional expansion instances, // it might be possible to add to the existing modularize error messages // (the second part referring to definitions being different), attempting // to tie them to the last macro conflict encountered with respect to the // order of the code encountered. // //===--------------------------------------------------------------------===// #include "PreprocessorTracker.h" #include "ModularizeUtilities.h" #include "clang/Lex/LexDiagnostic.h" #include "clang/Lex/MacroArgs.h" #include "clang/Lex/PPCallbacks.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/raw_ostream.h" namespace Modularize { // Some handle types typedef llvm::StringRef StringHandle; typedef int HeaderHandle; const HeaderHandle HeaderHandleInvalid = -1; 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. static 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 given a location. static std::string getSourceLine(clang::Preprocessor &PP, clang::SourceLocation Loc) { llvm::MemoryBufferRef MemBuffer = PP.getSourceManager().getBufferOrFake( 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(); } // Retrieve source line from file image given a file ID and line number. static std::string getSourceLine(clang::Preprocessor &PP, clang::FileID FileID, int Line) { llvm::MemoryBufferRef MemBuffer = PP.getSourceManager().getBufferOrFake(FileID); const char *Buffer = MemBuffer.getBufferStart(); const char *BufferEnd = MemBuffer.getBufferEnd(); const char *BeginPtr = Buffer; const char *EndPtr = BufferEnd; int LineCounter = 1; if (Line == 1) BeginPtr = Buffer; else { while (Buffer < BufferEnd) { if (*Buffer == '\n') { if (++LineCounter == Line) { BeginPtr = Buffer++ + 1; break; } } Buffer++; } } while (Buffer < BufferEnd) { if (*Buffer == '\n') { EndPtr = Buffer; break; } Buffer++; } size_t Length = EndPtr - BeginPtr; return llvm::StringRef(BeginPtr, Length).str(); } // Get the string for the Unexpanded macro instance. // The sourceRange 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. static 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. // FIXME: This doesn't support function-style macro instances // passed as arguments to another function-style macro. However, // since it still expands the inner arguments, it still // allows modularize to effectively work with respect to macro // consistency checking, although it displays the incorrect // expansion in error messages. static 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. for (const auto &T : MI->tokens()) { clang::IdentifierInfo *II = T.getIdentifierInfo(); int ArgNo = (II && Args ? MI->getParameterNum(II) : -1); if (ArgNo == -1) { // This isn't an argument, just add it. if (II == nullptr) Expanded += PP.getSpelling(T); // 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 && (Name != MacroName)) Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr); 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, 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 == nullptr) 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) Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr); else Expanded += Name; } } } return Expanded; } namespace { // ConditionValueKind strings. const char * ConditionValueKindStrings[] = { "(not evaluated)", "false", "true" }; // 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 to the header. 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 (auto 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 values 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 (auto I = MacroExpansionInstances.begin(), E = MacroExpansionInstances.end(); I != E; ++I) { if ((I->MacroExpanded == MacroExpanded) && (I->DefinitionLocation == DefinitionLocation)) { return &*I; // Found. } } return nullptr; // 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 condition exoression result // with a unique value. It also stores the unique header inclusion paths // for use in telling the user the nested include path to the header. class ConditionalExpansionInstance { public: ConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind 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 (auto 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. clang::PPCallbacks::ConditionValueKind 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 ConditionExpansionInstance objects representing // the unique value the condition expression expands to in instances of // the header. class ConditionalTracker { public: ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind, clang::PPCallbacks::ConditionValueKind ConditionValue, StringHandle ConditionUnexpanded, InclusionPathHandle InclusionPathHandle) : DirectiveKind(DirectiveKind), ConditionUnexpanded(ConditionUnexpanded) { addConditionalExpansionInstance(ConditionValue, InclusionPathHandle); } ConditionalTracker() {} // Find a matching condition expansion instance. ConditionalExpansionInstance * findConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue) { for (auto I = ConditionalExpansionInstances.begin(), E = ConditionalExpansionInstances.end(); I != E; ++I) { if (I->ConditionValue == ConditionValue) { return &*I; // Found. } } return nullptr; // Not found. } // Add a conditional expansion instance. void addConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind 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; }; class PreprocessorTrackerImpl; // 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() override {} // Overridden handlers. void InclusionDirective(clang::SourceLocation HashLoc, const clang::Token &IncludeTok, llvm::StringRef FileName, bool IsAngled, clang::CharSourceRange FilenameRange, const clang::FileEntry *File, llvm::StringRef SearchPath, llvm::StringRef RelativePath, const clang::Module *Imported, clang::SrcMgr::CharacteristicKind FileType) override; void FileChanged(clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason, clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID = clang::FileID()) override; void MacroExpands(const clang::Token &MacroNameTok, const clang::MacroDefinition &MD, clang::SourceRange Range, const clang::MacroArgs *Args) override; void Defined(const clang::Token &MacroNameTok, const clang::MacroDefinition &MD, clang::SourceRange Range) override; void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange, clang::PPCallbacks::ConditionValueKind ConditionResult) override; void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange, clang::PPCallbacks::ConditionValueKind ConditionResult, clang::SourceLocation IfLoc) override; void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, const clang::MacroDefinition &MD) override; void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, const clang::MacroDefinition &MD) override; 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(llvm::SmallVector &Headers, bool DoBlockCheckHeaderListOnly) : BlockCheckHeaderListOnly(DoBlockCheckHeaderListOnly), CurrentInclusionPathHandle(InclusionPathHandleInvalid), InNestedHeader(false) { // Use canonical header path representation. for (llvm::ArrayRef::iterator I = Headers.begin(), E = Headers.end(); I != E; ++I) { HeaderList.push_back(getCanonicalPath(*I)); } } ~PreprocessorTrackerImpl() override {} // Handle entering a preprocessing session. void handlePreprocessorEntry(clang::Preprocessor &PP, llvm::StringRef rootHeaderFile) override { HeadersInThisCompile.clear(); assert((HeaderStack.size() == 0) && "Header stack should be empty."); pushHeaderHandle(addHeader(rootHeaderFile)); PP.addPPCallbacks(std::make_unique(*this, PP, rootHeaderFile)); } // Handle exiting a preprocessing session. void handlePreprocessorExit() override { HeaderStack.clear(); } // Handle include directive. // This function is called every time an include directive is seen by the // preprocessor, for the purpose of later checking for 'extern "" {}' or // "namespace {}" blocks containing #include directives. void handleIncludeDirective(llvm::StringRef DirectivePath, int DirectiveLine, int DirectiveColumn, llvm::StringRef TargetPath) override { // If it's not a header in the header list, ignore it with respect to // the check. if (BlockCheckHeaderListOnly && !isHeaderListHeader(TargetPath)) return; HeaderHandle CurrentHeaderHandle = findHeaderHandle(DirectivePath); StringHandle IncludeHeaderHandle = addString(TargetPath); for (std::vector::const_iterator I = IncludeDirectives.begin(), E = IncludeDirectives.end(); I != E; ++I) { // If we already have an entry for this directive, return now. if ((I->File == CurrentHeaderHandle) && (I->Line == DirectiveLine)) return; } PPItemKey IncludeDirectiveItem(IncludeHeaderHandle, CurrentHeaderHandle, DirectiveLine, DirectiveColumn); IncludeDirectives.push_back(IncludeDirectiveItem); } // Check for include directives within the given source line range. // Report errors if any found. Returns true if no include directives // found in block. bool checkForIncludesInBlock(clang::Preprocessor &PP, clang::SourceRange BlockSourceRange, const char *BlockIdentifierMessage, llvm::raw_ostream &OS) override { clang::SourceLocation BlockStartLoc = BlockSourceRange.getBegin(); clang::SourceLocation BlockEndLoc = BlockSourceRange.getEnd(); // Use block location to get FileID of both the include directive // and block statement. clang::FileID FileID = PP.getSourceManager().getFileID(BlockStartLoc); std::string SourcePath = getSourceLocationFile(PP, BlockStartLoc); SourcePath = ModularizeUtilities::getCanonicalPath(SourcePath); HeaderHandle SourceHandle = findHeaderHandle(SourcePath); if (SourceHandle == -1) return true; int BlockStartLine, BlockStartColumn, BlockEndLine, BlockEndColumn; bool returnValue = true; getSourceLocationLineAndColumn(PP, BlockStartLoc, BlockStartLine, BlockStartColumn); getSourceLocationLineAndColumn(PP, BlockEndLoc, BlockEndLine, BlockEndColumn); for (std::vector::const_iterator I = IncludeDirectives.begin(), E = IncludeDirectives.end(); I != E; ++I) { // If we find an entry within the block, report an error. if ((I->File == SourceHandle) && (I->Line >= BlockStartLine) && (I->Line < BlockEndLine)) { returnValue = false; OS << SourcePath << ":" << I->Line << ":" << I->Column << ":\n"; OS << getSourceLine(PP, FileID, I->Line) << "\n"; if (I->Column > 0) OS << std::string(I->Column - 1, ' ') << "^\n"; OS << "error: Include directive within " << BlockIdentifierMessage << ".\n"; OS << SourcePath << ":" << BlockStartLine << ":" << BlockStartColumn << ":\n"; OS << getSourceLine(PP, BlockStartLoc) << "\n"; if (BlockStartColumn > 0) OS << std::string(BlockStartColumn - 1, ' ') << "^\n"; OS << "The \"" << BlockIdentifierMessage << "\" block is here.\n"; } } return returnValue; } // 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); // Check for nested header. if (!InNestedHeader) InNestedHeader = !HeadersInThisCompile.insert(H).second; } // 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); HeaderHandle TH; if (isHeaderHandleInStack(H)) { do { TH = getCurrentHeaderHandle(); popHeaderHandle(); } while ((TH != H) && (HeaderStack.size() != 0)); } InNestedHeader = false; } // Lookup/add string. StringHandle addString(llvm::StringRef Str) { return Strings.insert(Str).first->first(); } // Convert to a canonical path. std::string getCanonicalPath(llvm::StringRef path) const { std::string CanonicalPath(path); std::replace(CanonicalPath.begin(), CanonicalPath.end(), '\\', '/'); return CanonicalPath; } // Return true if the given header is in the header list. bool isHeaderListHeader(llvm::StringRef HeaderPath) const { std::string CanonicalPath = getCanonicalPath(HeaderPath); for (llvm::ArrayRef::iterator I = HeaderList.begin(), E = HeaderList.end(); I != E; ++I) { if (*I == CanonicalPath) return true; } return false; } // Get the handle of a header file entry. // Return HeaderHandleInvalid if not found. HeaderHandle findHeaderHandle(llvm::StringRef HeaderPath) const { std::string CanonicalPath = getCanonicalPath(HeaderPath); HeaderHandle H = 0; for (auto I = HeaderPaths.begin(), E = HeaderPaths.end(); I != E; ++I, ++H) { if (*I == CanonicalPath) 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 = getCanonicalPath(HeaderPath); 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 (auto 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 (auto 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) { if (InNestedHeader) return; StringHandle MacroName = addString(II->getName()); PPItemKey InstanceKey(PP, MacroName, H, InstanceLoc); PPItemKey DefinitionKey(PP, MacroName, H, DefinitionLoc); auto I = MacroExpansions.find(InstanceKey); // If existing instance of expansion not found, add one. 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 { // We've seen the macro before. Get its tracker. MacroExpansionTracker &CondTracker = I->second; // Look up an existing instance value for the macro. MacroExpansionInstance *MacroInfo = CondTracker.findMacroExpansionInstance(addString(MacroExpanded), DefinitionKey); // If found, just add the inclusion path to the instance. if (MacroInfo) MacroInfo->addInclusionPathHandle(InclusionPathHandle); else { // Otherwise add a new instance with the unique value. 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, clang::PPCallbacks::ConditionValueKind ConditionValue, llvm::StringRef ConditionUnexpanded, InclusionPathHandle InclusionPathHandle) { // Ignore header guards, assuming the header guard is the only conditional. if (InNestedHeader) return; StringHandle ConditionUnexpandedHandle(addString(ConditionUnexpanded)); PPItemKey InstanceKey(PP, ConditionUnexpandedHandle, H, InstanceLoc); auto I = ConditionalExpansions.find(InstanceKey); // If existing instance of condition not found, add one. if (I == ConditionalExpansions.end()) { std::string InstanceSourceLine = getSourceLocationString(PP, InstanceLoc) + ":\n" + getSourceLine(PP, InstanceLoc) + "\n"; ConditionalExpansions[InstanceKey] = ConditionalTracker(DirectiveKind, ConditionValue, ConditionUnexpandedHandle, InclusionPathHandle); } else { // We've seen the conditional before. Get its tracker. ConditionalTracker &CondTracker = I->second; // Look up an existing instance value for the condition. ConditionalExpansionInstance *MacroInfo = CondTracker.findConditionalExpansionInstance(ConditionValue); // If found, just add the inclusion path to the instance. if (MacroInfo) MacroInfo->addInclusionPathHandle(InclusionPathHandle); else { // Otherwise add a new instance with the unique value. CondTracker.addConditionalExpansionInstance(ConditionValue, InclusionPathHandle); } } } // Report on inconsistent macro instances. // Returns true if any mismatches. bool reportInconsistentMacros(llvm::raw_ostream &OS) override { bool ReturnValue = false; // Walk all the macro expansion trackers in the map. for (auto I = MacroExpansions.begin(), E = MacroExpansions.end(); I != E; ++I) { const PPItemKey &ItemKey = I->first; MacroExpansionTracker &MacroExpTracker = I->second; // If no mismatch (only one instance value) continue. if (!MacroExpTracker.hasMismatch()) continue; // Tell caller we found one or more errors. ReturnValue = true; // Start the error message. 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"; // Walk all the instances. for (auto 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"; // Walk all the inclusion path hierarchies. for (auto IIP = MacroInfo.InclusionPathHandles.begin(), EIP = MacroInfo.InclusionPathHandles.end(); IIP != EIP; ++IIP) { const std::vector &ip = getInclusionPath(*IIP); auto 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) override { bool ReturnValue = false; // Walk all the conditional trackers in the map. for (auto I = ConditionalExpansions.begin(), E = ConditionalExpansions.end(); I != E; ++I) { const PPItemKey &ItemKey = I->first; ConditionalTracker &CondTracker = I->second; if (!CondTracker.hasMismatch()) continue; // Tell caller we found one or more errors. ReturnValue = true; // Start the error message. 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"; // Walk all the instances. for (auto IMT = CondTracker.ConditionalExpansionInstances.begin(), EMT = CondTracker.ConditionalExpansionInstances.end(); IMT != EMT; ++IMT) { ConditionalExpansionInstance &MacroInfo = *IMT; OS << " '" << CondTracker.ConditionUnexpanded << "' expanded to: '" << ConditionValueKindStrings[MacroInfo.ConditionValue] << "' with respect to these inclusion paths:\n"; // Walk all the inclusion path hierarchies. for (auto IIP = MacroInfo.InclusionPathHandles.begin(), EIP = MacroInfo.InclusionPathHandles.end(); IIP != EIP; ++IIP) { const std::vector &ip = getInclusionPath(*IIP); auto 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::SmallVector HeaderList; // Only do extern, namespace check for headers in HeaderList. bool BlockCheckHeaderListOnly; llvm::StringSet<> Strings; std::vector HeaderPaths; std::vector HeaderStack; std::vector InclusionPaths; InclusionPathHandle CurrentInclusionPathHandle; llvm::SmallSet HeadersInThisCompile; std::vector IncludeDirectives; MacroExpansionMap MacroExpansions; ConditionalExpansionMap ConditionalExpansions; bool InNestedHeader; }; } // namespace // PreprocessorTracker functions. // PreprocessorTracker destructor. PreprocessorTracker::~PreprocessorTracker() {} // Create instance of PreprocessorTracker. PreprocessorTracker *PreprocessorTracker::create( llvm::SmallVector &Headers, bool DoBlockCheckHeaderListOnly) { return new PreprocessorTrackerImpl(Headers, DoBlockCheckHeaderListOnly); } // Preprocessor callbacks for modularize. // Handle include directive. void PreprocessorCallbacks::InclusionDirective( clang::SourceLocation HashLoc, const clang::Token &IncludeTok, llvm::StringRef FileName, bool IsAngled, clang::CharSourceRange FilenameRange, const clang::FileEntry *File, llvm::StringRef SearchPath, llvm::StringRef RelativePath, const clang::Module *Imported, clang::SrcMgr::CharacteristicKind FileType) { int DirectiveLine, DirectiveColumn; std::string HeaderPath = getSourceLocationFile(PP, HashLoc); getSourceLocationLineAndColumn(PP, HashLoc, DirectiveLine, DirectiveColumn); PPTracker.handleIncludeDirective(HeaderPath, DirectiveLine, DirectiveColumn, FileName); } // 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: { const clang::FileEntry *F = PP.getSourceManager().getFileEntryForID(PrevFID); if (F) PPTracker.handleHeaderExit(F->getName()); } break; case SystemHeaderPragma: case RenameFile: break; } } // Handle macro expansion. void PreprocessorCallbacks::MacroExpands(const clang::Token &MacroNameTok, const clang::MacroDefinition &MD, clang::SourceRange Range, const clang::MacroArgs *Args) { clang::SourceLocation Loc = Range.getBegin(); // Ignore macro argument expansions. if (!Loc.isFileID()) return; clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo(); const clang::MacroInfo *MI = MD.getMacroInfo(); 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::MacroDefinition &MD, clang::SourceRange Range) { clang::SourceLocation Loc(Range.getBegin()); clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo(); const clang::MacroInfo *MI = MD.getMacroInfo(); 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, clang::PPCallbacks::ConditionValueKind 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, clang::PPCallbacks::ConditionValueKind 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::MacroDefinition &MD) { clang::PPCallbacks::ConditionValueKind IsDefined = (MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False ); 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::MacroDefinition &MD) { clang::PPCallbacks::ConditionValueKind IsNotDefined = (!MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False ); PPTracker.addConditionalExpansionInstance( PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifndef, IsNotDefined, PP.getSpelling(MacroNameTok), PPTracker.getCurrentInclusionPathHandle()); } } // end namespace Modularize