| //===--- 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<clang::MacroArgs *>(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<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 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<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 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<MacroExpansionInstance> 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<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 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<ConditionalExpansionInstance> 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<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(llvm::SmallVector<std::string, 32> &Headers, |
| bool DoBlockCheckHeaderListOnly) |
| : BlockCheckHeaderListOnly(DoBlockCheckHeaderListOnly), |
| CurrentInclusionPathHandle(InclusionPathHandleInvalid), |
| InNestedHeader(false) { |
| // Use canonical header path representation. |
| for (llvm::ArrayRef<std::string>::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<PreprocessorCallbacks>(*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<PPItemKey>::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<PPItemKey>::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 <built-in> and <command-line> 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 <built-in> and <command-line> 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<std::string>::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<HeaderHandle> &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<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) { |
| 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<HeaderHandle> &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<HeaderHandle> &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<std::string, 32> HeaderList; |
| // Only do extern, namespace check for headers in HeaderList. |
| bool BlockCheckHeaderListOnly; |
| llvm::StringSet<> Strings; |
| std::vector<StringHandle> HeaderPaths; |
| std::vector<HeaderHandle> HeaderStack; |
| std::vector<HeaderInclusionPath> InclusionPaths; |
| InclusionPathHandle CurrentInclusionPathHandle; |
| llvm::SmallSet<HeaderHandle, 32> HeadersInThisCompile; |
| std::vector<PPItemKey> IncludeDirectives; |
| MacroExpansionMap MacroExpansions; |
| ConditionalExpansionMap ConditionalExpansions; |
| bool InNestedHeader; |
| }; |
| |
| } // namespace |
| |
| // PreprocessorTracker functions. |
| |
| // PreprocessorTracker destructor. |
| PreprocessorTracker::~PreprocessorTracker() {} |
| |
| // Create instance of PreprocessorTracker. |
| PreprocessorTracker *PreprocessorTracker::create( |
| llvm::SmallVector<std::string, 32> &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 |