brintos

brintos / llvm-project-archived public Read only

0
0
Text · 57.7 KiB · 04abb27 Raw
1367 lines · cpp
1//===--- PreprocessorTracker.cpp - Preprocessor tracking -*- C++ -*------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===--------------------------------------------------------------------===//8//9// The Basic Idea (Macro and Conditional Checking)10//11// Basically we install a PPCallbacks-derived object to track preprocessor12// activity, namely when a header file is entered/exited, when a macro13// is expanded, when "defined" is used, and when #if, #elif, #ifdef,14// and #ifndef are used.  We save the state of macro and "defined"15// expressions in a map, keyed on a name/file/line/column quadruple.16// The map entries store the different states (values) that a macro expansion,17// "defined" expression, or condition expression has in the course of18// processing for the one location in the one header containing it,19// plus a list of the nested include stacks for the states.  When a macro20// or "defined" expression evaluates to the same value, which is the21// desired case, only one state is stored.  Similarly, for conditional22// directives, we save the condition expression states in a separate map.23//24// This information is collected as modularize compiles all the headers25// given to it to process.  After all the compilations are performed,26// a check is performed for any entries in the maps that contain more27// than one different state, and for these an output message is generated.28//29// For example:30//31//   (...)/SubHeader.h:11:5:32//   #if SYMBOL == 133//       ^34//   error: Macro instance 'SYMBOL' has different values in this header,35//          depending on how it was included.36//     'SYMBOL' expanded to: '1' with respect to these inclusion paths:37//       (...)/Header1.h38//         (...)/SubHeader.h39//   (...)/SubHeader.h:3:9:40//   #define SYMBOL 141//             ^42//   Macro defined here.43//     'SYMBOL' expanded to: '2' with respect to these inclusion paths:44//       (...)/Header2.h45//           (...)/SubHeader.h46//   (...)/SubHeader.h:7:9:47//   #define SYMBOL 248//             ^49//   Macro defined here.50//51// The Basic Idea ('Extern "C/C++" {}' Or 'namespace {}') With Nested52// '#include' Checking)53//54// To check for '#include' directives nested inside 'Extern "C/C++" {}'55// or 'namespace {}' blocks, we keep track of the '#include' directives56// while running the preprocessor, and later during a walk of the AST57// we call a function to check for any '#include' directives inside58// an 'Extern "C/C++" {}' or 'namespace {}' block, given its source59// range.60//61// Design and Implementation Details (Macro and Conditional Checking)62//63// A PreprocessorTrackerImpl class implements the PreprocessorTracker64// interface. It uses a PreprocessorCallbacks class derived from PPCallbacks65// to track preprocessor activity, namely entering/exiting a header, macro66// expansions, use of "defined" expressions, and #if, #elif, #ifdef, and67// #ifndef conditional directives. PreprocessorTrackerImpl stores a map68// of MacroExpansionTracker objects keyed on a name/file/line/column69// value represented by a light-weight PPItemKey value object. This70// is the key top-level data structure tracking the values of macro71// expansion instances.  Similarly, it stores a map of ConditionalTracker72// objects with the same kind of key, for tracking preprocessor conditional73// directives.74//75// The MacroExpansionTracker object represents one macro reference or use76// of a "defined" expression in a header file. It stores a handle to a77// string representing the unexpanded macro instance, a handle to a string78// representing the unpreprocessed source line containing the unexpanded79// macro instance, and a vector of one or more MacroExpansionInstance80// objects.81//82// The MacroExpansionInstance object represents one or more expansions83// of a macro reference, for the case where the macro expands to the same84// value. MacroExpansionInstance stores a handle to a string representing85// the expanded macro value, a PPItemKey representing the file/line/column86// where the macro was defined, a handle to a string representing the source87// line containing the macro definition, and a vector of InclusionPathHandle88// values that represents the hierarchies of include files for each case89// where the particular header containing the macro reference was referenced90// or included.91 92// In the normal case where a macro instance always expands to the same93// value, the MacroExpansionTracker object will only contain one94// MacroExpansionInstance representing all the macro expansion instances.95// If a case was encountered where a macro instance expands to a value96// that is different from that seen before, or the macro was defined in97// a different place, a new MacroExpansionInstance object representing98// that case will be added to the vector in MacroExpansionTracker. If a99// macro instance expands to a value already seen before, the100// InclusionPathHandle representing that case's include file hierarchy101// will be added to the existing MacroExpansionInstance object.102 103// For checking conditional directives, the ConditionalTracker class104// functions similarly to MacroExpansionTracker, but tracks an #if,105// #elif, #ifdef, or #ifndef directive in a header file.  It stores106// a vector of one or two ConditionalExpansionInstance objects,107// representing the cases where the conditional expression evaluates108// to true or false.  This latter object stores the evaluated value109// of the condition expression (a bool) and a vector of110// InclusionPathHandles.111//112// To reduce the instances of string and object copying, the113// PreprocessorTrackerImpl class uses a StringPool to save all stored114// strings, and defines a StringHandle type to abstract the references115// to the strings.116//117// PreprocessorTrackerImpl also maintains a list representing the unique118// headers, which is just a vector of StringHandle's for the header file119// paths. A HeaderHandle abstracts a reference to a header, and is simply120// the index of the stored header file path.121//122// A HeaderInclusionPath class abstracts a unique hierarchy of header file123// inclusions. It simply stores a vector of HeaderHandles ordered from the124// top-most header (the one from the header list passed to modularize) down125// to the header containing the macro reference. PreprocessorTrackerImpl126// stores a vector of these objects. An InclusionPathHandle typedef127// abstracts a reference to one of the HeaderInclusionPath objects, and is128// simply the index of the stored HeaderInclusionPath object. The129// MacroExpansionInstance object stores a vector of these handles so that130// the reporting function can display the include hierarchies for the macro131// expansion instances represented by that object, to help the user132// understand how the header was included. (A future enhancement might133// be to associate a line number for the #include directives, but I134// think not doing so is good enough for the present.)135//136// A key reason for using these opaque handles was to try to keep all the137// internal objects light-weight value objects, in order to reduce string138// and object copying overhead, and to abstract this implementation detail.139//140// The key data structures are built up while modularize runs the headers141// through the compilation. A PreprocessorTracker instance is created and142// passed down to the AST action and consumer objects in modularize. For143// each new compilation instance, the consumer calls the144// PreprocessorTracker's handleNewPreprocessorEntry function, which sets145// up a PreprocessorCallbacks object for the preprocessor. At the end of146// the compilation instance, the PreprocessorTracker's147// handleNewPreprocessorExit function handles cleaning up with respect148// to the preprocessing instance.149//150// The PreprocessorCallbacks object uses an overridden FileChanged callback151// to determine when a header is entered and exited (including exiting the152// header during #include directives). It calls PreprocessorTracker's153// handleHeaderEntry and handleHeaderExit functions upon entering and154// exiting a header. These functions manage a stack of header handles155// representing by a vector, pushing and popping header handles as headers156// are entered and exited. When a HeaderInclusionPath object is created,157// it simply copies this stack.158//159// The PreprocessorCallbacks object uses an overridden MacroExpands callback160// to track when a macro expansion is performed. It calls a couple of helper161// functions to get the unexpanded and expanded macro values as strings, but162// then calls PreprocessorTrackerImpl's addMacroExpansionInstance function to163// do the rest of the work. The getMacroExpandedString function uses the164// preprocessor's getSpelling to convert tokens to strings using the165// information passed to the MacroExpands callback, and simply concatenates166// them. It makes recursive calls to itself to handle nested macro167// definitions, and also handles function-style macros.168//169// PreprocessorTrackerImpl's addMacroExpansionInstance function looks for170// an existing MacroExpansionTracker entry in its map of MacroExampleTracker171// objects. If none exists, it adds one with one MacroExpansionInstance and172// returns. If a MacroExpansionTracker object already exists, it looks for173// an existing MacroExpansionInstance object stored in the174// MacroExpansionTracker object, one that matches the macro expanded value175// and the macro definition location. If a matching MacroExpansionInstance176// object is found, it just adds the current HeaderInclusionPath object to177// it. If not found, it creates and stores a new MacroExpansionInstance178// object. The addMacroExpansionInstance function calls a couple of helper179// functions to get the pre-formatted location and source line strings for180// the macro reference and the macro definition stored as string handles.181// These helper functions use the current source manager from the182// preprocessor. This is done in advance at this point in time because the183// source manager doesn't exist at the time of the reporting.184//185// For conditional check, the PreprocessorCallbacks class overrides the186// PPCallbacks handlers for #if, #elif, #ifdef, and #ifndef.  These handlers187// call the addConditionalExpansionInstance method of188// PreprocessorTrackerImpl.  The process is similar to that of macros, but189// with some different data and error messages.  A lookup is performed for190// the conditional, and if a ConditionalTracker object doesn't yet exist for191// the conditional, a new one is added, including adding a192// ConditionalExpansionInstance object to it to represent the condition193// expression state.  If a ConditionalTracker for the conditional does194// exist, a lookup is made for a ConditionalExpansionInstance object195// matching the condition expression state.  If one exists, a196// HeaderInclusionPath is added to it.  Otherwise a new197// ConditionalExpansionInstance  entry is made.  If a ConditionalTracker198// has two ConditionalExpansionInstance objects, it means there was a199// conflict, meaning the conditional expression evaluated differently in200// one or more cases.201//202// After modularize has performed all the compilations, it enters a phase203// of error reporting. This new feature adds to this reporting phase calls204// to the PreprocessorTracker's reportInconsistentMacros and205// reportInconsistentConditionals functions. These functions walk the maps206// of MacroExpansionTracker's and ConditionalTracker's respectively. If207// any of these objects have more than one MacroExpansionInstance or208// ConditionalExpansionInstance objects, it formats and outputs an error209// message like the example shown previously, using the stored data.210//211// A potential issue is that there is some overlap between the #if/#elif212// conditional and macro reporting.  I could disable the #if and #elif,213// leaving just the #ifdef and #ifndef, since these don't overlap.  Or,214// to make clearer the separate reporting phases, I could add an output215// message marking the phases.216//217// Design and Implementation Details ('Extern "C/C++" {}' Or218// 'namespace {}') With Nested '#include' Checking)219//220// We override the InclusionDirective in PPCallbacks to record information221// about each '#include' directive encountered during preprocessing.222// We co-opt the PPItemKey class to store the information about each223// '#include' directive, including the source file name containing the224// directive, the name of the file being included, and the source line225// and column of the directive.  We store these object in a vector,226// after first check to see if an entry already exists.227//228// Later, while the AST is being walked for other checks, we provide229// visit handlers for 'extern "C/C++" {}' and 'namespace (name) {}'230// blocks, checking to see if any '#include' directives occurred231// within the blocks, reporting errors if any found.232//233// Future Directions234//235// We probably should add options to disable any of the checks, in case236// there is some problem with them, or the messages get too verbose.237//238// With the map of all the macro and conditional expansion instances,239// it might be possible to add to the existing modularize error messages240// (the second part referring to definitions being different), attempting241// to tie them to the last macro conflict encountered with respect to the242// order of the code encountered.243//244//===--------------------------------------------------------------------===//245 246#include "PreprocessorTracker.h"247#include "ModularizeUtilities.h"248#include "clang/Lex/LexDiagnostic.h"249#include "clang/Lex/MacroArgs.h"250#include "clang/Lex/PPCallbacks.h"251#include "llvm/ADT/SmallSet.h"252#include "llvm/ADT/StringSet.h"253#include "llvm/Support/raw_ostream.h"254 255namespace Modularize {256 257// Some handle types258typedef llvm::StringRef StringHandle;259 260typedef int HeaderHandle;261const HeaderHandle HeaderHandleInvalid = -1;262 263typedef int InclusionPathHandle;264const InclusionPathHandle InclusionPathHandleInvalid = -1;265 266// Some utility functions.267 268// Get a "file:line:column" source location string.269static std::string getSourceLocationString(clang::Preprocessor &PP,270                                           clang::SourceLocation Loc) {271  if (Loc.isInvalid())272    return std::string("(none)");273  else274    return Loc.printToString(PP.getSourceManager());275}276 277// Get just the file name from a source location.278static std::string getSourceLocationFile(clang::Preprocessor &PP,279                                         clang::SourceLocation Loc) {280  std::string Source(getSourceLocationString(PP, Loc));281  size_t Offset = Source.find(':', 2);282  if (Offset == std::string::npos)283    return Source;284  return Source.substr(0, Offset);285}286 287// Get just the line and column from a source location.288static void getSourceLocationLineAndColumn(clang::Preprocessor &PP,289                                           clang::SourceLocation Loc, int &Line,290                                           int &Column) {291  clang::PresumedLoc PLoc = PP.getSourceManager().getPresumedLoc(Loc);292  if (PLoc.isInvalid()) {293    Line = 0;294    Column = 0;295    return;296  }297  Line = PLoc.getLine();298  Column = PLoc.getColumn();299}300 301// Retrieve source snippet from file image.302static std::string getSourceString(clang::Preprocessor &PP,303                                   clang::SourceRange Range) {304  clang::SourceLocation BeginLoc = Range.getBegin();305  clang::SourceLocation EndLoc = Range.getEnd();306  const char *BeginPtr = PP.getSourceManager().getCharacterData(BeginLoc);307  const char *EndPtr = PP.getSourceManager().getCharacterData(EndLoc);308  size_t Length = EndPtr - BeginPtr;309  return llvm::StringRef(BeginPtr, Length).trim().str();310}311 312// Retrieve source line from file image given a location.313static std::string getSourceLine(clang::Preprocessor &PP,314                                 clang::SourceLocation Loc) {315  llvm::MemoryBufferRef MemBuffer = PP.getSourceManager().getBufferOrFake(316      PP.getSourceManager().getFileID(Loc));317  const char *Buffer = MemBuffer.getBufferStart();318  const char *BufferEnd = MemBuffer.getBufferEnd();319  const char *BeginPtr = PP.getSourceManager().getCharacterData(Loc);320  const char *EndPtr = BeginPtr;321  while (BeginPtr > Buffer) {322    if (*BeginPtr == '\n') {323      BeginPtr++;324      break;325    }326    BeginPtr--;327  }328  while (EndPtr < BufferEnd) {329    if (*EndPtr == '\n') {330      break;331    }332    EndPtr++;333  }334  size_t Length = EndPtr - BeginPtr;335  return llvm::StringRef(BeginPtr, Length).str();336}337 338// Retrieve source line from file image given a file ID and line number.339static std::string getSourceLine(clang::Preprocessor &PP, clang::FileID FileID,340                                 int Line) {341  llvm::MemoryBufferRef MemBuffer =342      PP.getSourceManager().getBufferOrFake(FileID);343  const char *Buffer = MemBuffer.getBufferStart();344  const char *BufferEnd = MemBuffer.getBufferEnd();345  const char *BeginPtr = Buffer;346  const char *EndPtr = BufferEnd;347  int LineCounter = 1;348  if (Line == 1)349    BeginPtr = Buffer;350  else {351    while (Buffer < BufferEnd) {352      if (*Buffer == '\n') {353        if (++LineCounter == Line) {354          BeginPtr = Buffer++ + 1;355          break;356        }357      }358      Buffer++;359    }360  }361  while (Buffer < BufferEnd) {362    if (*Buffer == '\n') {363      EndPtr = Buffer;364      break;365    }366    Buffer++;367  }368  size_t Length = EndPtr - BeginPtr;369  return llvm::StringRef(BeginPtr, Length).str();370}371 372// Get the string for the Unexpanded macro instance.373// The sourceRange is expected to end at the last token374// for the macro instance, which in the case of a function-style375// macro will be a ')', but for an object-style macro, it376// will be the macro name itself.377static std::string getMacroUnexpandedString(clang::SourceRange Range,378                                            clang::Preprocessor &PP,379                                            llvm::StringRef MacroName,380                                            const clang::MacroInfo *MI) {381  clang::SourceLocation BeginLoc(Range.getBegin());382  const char *BeginPtr = PP.getSourceManager().getCharacterData(BeginLoc);383  size_t Length;384  if (MI->isFunctionLike()) {385    clang::SourceLocation EndLoc(Range.getEnd());386    const char *EndPtr = PP.getSourceManager().getCharacterData(EndLoc) + 1;387    Length = (EndPtr - BeginPtr) + 1; // +1 is ')' width.388  } else389    Length = MacroName.size();390  return llvm::StringRef(BeginPtr, Length).trim().str();391}392 393// Get the expansion for a macro instance, given the information394// provided by PPCallbacks.395// FIXME: This doesn't support function-style macro instances396// passed as arguments to another function-style macro. However,397// since it still expands the inner arguments, it still398// allows modularize to effectively work with respect to macro399// consistency checking, although it displays the incorrect400// expansion in error messages.401static std::string getMacroExpandedString(clang::Preprocessor &PP,402                                          llvm::StringRef MacroName,403                                          const clang::MacroInfo *MI,404                                          const clang::MacroArgs *Args) {405  std::string Expanded;406  // Walk over the macro Tokens.407  for (const auto &T : MI->tokens()) {408    clang::IdentifierInfo *II = T.getIdentifierInfo();409    int ArgNo = (II && Args ? MI->getParameterNum(II) : -1);410    if (ArgNo == -1) {411      // This isn't an argument, just add it.412      if (II == nullptr)413        Expanded += PP.getSpelling(T); // Not an identifier.414      else {415        // Token is for an identifier.416        std::string Name = II->getName().str();417        // Check for nexted macro references.418        clang::MacroInfo *MacroInfo = PP.getMacroInfo(II);419        if (MacroInfo && (Name != MacroName))420          Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr);421        else422          Expanded += Name;423      }424      continue;425    }426    // We get here if it's a function-style macro with arguments.427    const clang::Token *ResultArgToks;428    const clang::Token *ArgTok = Args->getUnexpArgument(ArgNo);429    if (Args->ArgNeedsPreexpansion(ArgTok, PP))430      ResultArgToks = &(const_cast<clang::MacroArgs *>(Args))431          ->getPreExpArgument(ArgNo, PP)[0];432    else433      ResultArgToks = ArgTok; // Use non-preexpanded Tokens.434    // If the arg token didn't expand into anything, ignore it.435    if (ResultArgToks->is(clang::tok::eof))436      continue;437    unsigned NumToks = clang::MacroArgs::getArgLength(ResultArgToks);438    // Append the resulting argument expansions.439    for (unsigned ArgumentIndex = 0; ArgumentIndex < NumToks; ++ArgumentIndex) {440      const clang::Token &AT = ResultArgToks[ArgumentIndex];441      clang::IdentifierInfo *II = AT.getIdentifierInfo();442      if (II == nullptr)443        Expanded += PP.getSpelling(AT); // Not an identifier.444      else {445        // It's an identifier.  Check for further expansion.446        std::string Name = II->getName().str();447        clang::MacroInfo *MacroInfo = PP.getMacroInfo(II);448        if (MacroInfo)449          Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr);450        else451          Expanded += Name;452      }453    }454  }455  return Expanded;456}457 458namespace {459 460// ConditionValueKind strings.461const char *462ConditionValueKindStrings[] = {463  "(not evaluated)", "false", "true"464};465 466// Preprocessor item key.467//468// This class represents a location in a source file, for use469// as a key representing a unique name/file/line/column quadruplet,470// which in this case is used to identify a macro expansion instance,471// but could be used for other things as well.472// The file is a header file handle, the line is a line number,473// and the column is a column number.474class PPItemKey {475public:476  PPItemKey(clang::Preprocessor &PP, StringHandle Name, HeaderHandle File,477            clang::SourceLocation Loc)478      : Name(Name), File(File) {479    getSourceLocationLineAndColumn(PP, Loc, Line, Column);480  }481  PPItemKey(StringHandle Name, HeaderHandle File, int Line, int Column)482      : Name(Name), File(File), Line(Line), Column(Column) {}483  PPItemKey(const PPItemKey &Other)484      : Name(Other.Name), File(Other.File), Line(Other.Line),485        Column(Other.Column) {}486  PPItemKey() : File(HeaderHandleInvalid), Line(0), Column(0) {}487  bool operator==(const PPItemKey &Other) const {488    if (Name != Other.Name)489      return false;490    if (File != Other.File)491      return false;492    if (Line != Other.Line)493      return false;494    return Column == Other.Column;495  }496  bool operator<(const PPItemKey &Other) const {497    return std::tie(Name, File, Line, Column) <498           std::tie(Other.Name, Other.File, Other.Line, Other.Column);499  }500  StringHandle Name;501  HeaderHandle File;502  int Line;503  int Column;504};505 506// Header inclusion path.507class HeaderInclusionPath {508public:509  HeaderInclusionPath(std::vector<HeaderHandle> HeaderInclusionPath)510      : Path(HeaderInclusionPath) {}511  HeaderInclusionPath(const HeaderInclusionPath &Other) : Path(Other.Path) {}512  HeaderInclusionPath() {}513  std::vector<HeaderHandle> Path;514};515 516// Macro expansion instance.517//518// This class represents an instance of a macro expansion with a519// unique value.  It also stores the unique header inclusion paths520// for use in telling the user the nested include path to the header.521class MacroExpansionInstance {522public:523  MacroExpansionInstance(StringHandle MacroExpanded,524                         PPItemKey &DefinitionLocation,525                         StringHandle DefinitionSourceLine,526                         InclusionPathHandle H)527      : MacroExpanded(MacroExpanded), DefinitionLocation(DefinitionLocation),528        DefinitionSourceLine(DefinitionSourceLine) {529    InclusionPathHandles.push_back(H);530  }531  MacroExpansionInstance() {}532 533  // Check for the presence of a header inclusion path handle entry.534  // Return false if not found.535  bool haveInclusionPathHandle(InclusionPathHandle H) {536    for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();537         I != E; ++I) {538      if (*I == H)539        return true;540    }541    return InclusionPathHandleInvalid;542  }543  // Add a new header inclusion path entry, if not already present.544  void addInclusionPathHandle(InclusionPathHandle H) {545    if (!haveInclusionPathHandle(H))546      InclusionPathHandles.push_back(H);547  }548 549  // A string representing the macro instance after preprocessing.550  StringHandle MacroExpanded;551  // A file/line/column triplet representing the macro definition location.552  PPItemKey DefinitionLocation;553  // A place to save the macro definition line string.554  StringHandle DefinitionSourceLine;555  // The header inclusion path handles for all the instances.556  std::vector<InclusionPathHandle> InclusionPathHandles;557};558 559// Macro expansion instance tracker.560//561// This class represents one macro expansion, keyed by a PPItemKey.562// It stores a string representing the macro reference in the source,563// and a list of ConditionalExpansionInstances objects representing564// the unique values the condition expands to in instances of the header.565class MacroExpansionTracker {566public:567  MacroExpansionTracker(StringHandle MacroUnexpanded,568                        StringHandle MacroExpanded,569                        StringHandle InstanceSourceLine,570                        PPItemKey &DefinitionLocation,571                        StringHandle DefinitionSourceLine,572                        InclusionPathHandle InclusionPathHandle)573      : MacroUnexpanded(MacroUnexpanded),574        InstanceSourceLine(InstanceSourceLine) {575    addMacroExpansionInstance(MacroExpanded, DefinitionLocation,576                              DefinitionSourceLine, InclusionPathHandle);577  }578  MacroExpansionTracker() {}579 580  // Find a matching macro expansion instance.581  MacroExpansionInstance *582  findMacroExpansionInstance(StringHandle MacroExpanded,583                             PPItemKey &DefinitionLocation) {584    for (auto I = MacroExpansionInstances.begin(),585              E = MacroExpansionInstances.end();586         I != E; ++I) {587      if ((I->MacroExpanded == MacroExpanded) &&588          (I->DefinitionLocation == DefinitionLocation)) {589        return &*I; // Found.590      }591    }592    return nullptr; // Not found.593  }594 595  // Add a macro expansion instance.596  void addMacroExpansionInstance(StringHandle MacroExpanded,597                                 PPItemKey &DefinitionLocation,598                                 StringHandle DefinitionSourceLine,599                                 InclusionPathHandle InclusionPathHandle) {600    MacroExpansionInstances.push_back(601        MacroExpansionInstance(MacroExpanded, DefinitionLocation,602                               DefinitionSourceLine, InclusionPathHandle));603  }604 605  // Return true if there is a mismatch.606  bool hasMismatch() { return MacroExpansionInstances.size() > 1; }607 608  // A string representing the macro instance without expansion.609  StringHandle MacroUnexpanded;610  // A place to save the macro instance source line string.611  StringHandle InstanceSourceLine;612  // The macro expansion instances.613  // If all instances of the macro expansion expand to the same value,614  // This vector will only have one instance.615  std::vector<MacroExpansionInstance> MacroExpansionInstances;616};617 618// Conditional expansion instance.619//620// This class represents an instance of a condition exoression result621// with a unique value.  It also stores the unique header inclusion paths622// for use in telling the user the nested include path to the header.623class ConditionalExpansionInstance {624public:625  ConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue, InclusionPathHandle H)626      : ConditionValue(ConditionValue) {627    InclusionPathHandles.push_back(H);628  }629  ConditionalExpansionInstance() {}630 631  // Check for the presence of a header inclusion path handle entry.632  // Return false if not found.633  bool haveInclusionPathHandle(InclusionPathHandle H) {634    for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();635         I != E; ++I) {636      if (*I == H)637        return true;638    }639    return InclusionPathHandleInvalid;640  }641  // Add a new header inclusion path entry, if not already present.642  void addInclusionPathHandle(InclusionPathHandle H) {643    if (!haveInclusionPathHandle(H))644      InclusionPathHandles.push_back(H);645  }646 647  // A flag representing the evaluated condition value.648  clang::PPCallbacks::ConditionValueKind ConditionValue;649  // The header inclusion path handles for all the instances.650  std::vector<InclusionPathHandle> InclusionPathHandles;651};652 653// Conditional directive instance tracker.654//655// This class represents one conditional directive, keyed by a PPItemKey.656// It stores a string representing the macro reference in the source,657// and a list of ConditionExpansionInstance objects representing658// the unique value the condition expression expands to in instances of659// the header.660class ConditionalTracker {661public:662  ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind,663                     clang::PPCallbacks::ConditionValueKind ConditionValue,664                     StringHandle ConditionUnexpanded,665                     InclusionPathHandle InclusionPathHandle)666      : DirectiveKind(DirectiveKind), ConditionUnexpanded(ConditionUnexpanded) {667    addConditionalExpansionInstance(ConditionValue, InclusionPathHandle);668  }669  ConditionalTracker() {}670 671  // Find a matching condition expansion instance.672  ConditionalExpansionInstance *673  findConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue) {674    for (auto I = ConditionalExpansionInstances.begin(),675              E = ConditionalExpansionInstances.end();676         I != E; ++I) {677      if (I->ConditionValue == ConditionValue) {678        return &*I; // Found.679      }680    }681    return nullptr; // Not found.682  }683 684  // Add a conditional expansion instance.685  void686  addConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue,687                                  InclusionPathHandle InclusionPathHandle) {688    ConditionalExpansionInstances.push_back(689        ConditionalExpansionInstance(ConditionValue, InclusionPathHandle));690  }691 692  // Return true if there is a mismatch.693  bool hasMismatch() { return ConditionalExpansionInstances.size() > 1; }694 695  // The kind of directive.696  clang::tok::PPKeywordKind DirectiveKind;697  // A string representing the macro instance without expansion.698  StringHandle ConditionUnexpanded;699  // The condition expansion instances.700  // If all instances of the conditional expression expand to the same value,701  // This vector will only have one instance.702  std::vector<ConditionalExpansionInstance> ConditionalExpansionInstances;703};704 705class PreprocessorTrackerImpl;706 707// Preprocessor callbacks for modularize.708//709// This class derives from the Clang PPCallbacks class to track preprocessor710// actions, such as changing files and handling preprocessor directives and711// macro expansions.  It has to figure out when a new header file is entered712// and left, as the provided handler is not particularly clear about it.713class PreprocessorCallbacks : public clang::PPCallbacks {714public:715  PreprocessorCallbacks(PreprocessorTrackerImpl &ppTracker,716                        clang::Preprocessor &PP, llvm::StringRef rootHeaderFile)717      : PPTracker(ppTracker), PP(PP), RootHeaderFile(rootHeaderFile) {}718  ~PreprocessorCallbacks() override {}719 720  // Overridden handlers.721  void722  InclusionDirective(clang::SourceLocation HashLoc,723                     const clang::Token &IncludeTok, llvm::StringRef FileName,724                     bool IsAngled, clang::CharSourceRange FilenameRange,725                     clang::OptionalFileEntryRef File,726                     llvm::StringRef SearchPath, llvm::StringRef RelativePath,727                     const clang::Module *SuggestedModule, bool ModuleImported,728                     clang::SrcMgr::CharacteristicKind FileType) override;729  void FileChanged(clang::SourceLocation Loc,730                   clang::PPCallbacks::FileChangeReason Reason,731                   clang::SrcMgr::CharacteristicKind FileType,732                   clang::FileID PrevFID = clang::FileID()) override;733  void MacroExpands(const clang::Token &MacroNameTok,734                    const clang::MacroDefinition &MD, clang::SourceRange Range,735                    const clang::MacroArgs *Args) override;736  void Defined(const clang::Token &MacroNameTok,737               const clang::MacroDefinition &MD,738               clang::SourceRange Range) override;739  void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange,740          clang::PPCallbacks::ConditionValueKind ConditionResult) override;741  void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange,742            clang::PPCallbacks::ConditionValueKind ConditionResult,743            clang::SourceLocation IfLoc) override;744  void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,745             const clang::MacroDefinition &MD) override;746  void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,747              const clang::MacroDefinition &MD) override;748 749private:750  PreprocessorTrackerImpl &PPTracker;751  clang::Preprocessor &PP;752  std::string RootHeaderFile;753};754 755// Preprocessor macro expansion item map types.756typedef std::map<PPItemKey, MacroExpansionTracker> MacroExpansionMap;757typedef std::map<PPItemKey, MacroExpansionTracker>::iterator758MacroExpansionMapIter;759 760// Preprocessor conditional expansion item map types.761typedef std::map<PPItemKey, ConditionalTracker> ConditionalExpansionMap;762typedef std::map<PPItemKey, ConditionalTracker>::iterator763ConditionalExpansionMapIter;764 765// Preprocessor tracker for modularize.766//767// This class stores information about all the headers processed in the768// course of running modularize.769class PreprocessorTrackerImpl : public PreprocessorTracker {770public:771  PreprocessorTrackerImpl(llvm::SmallVector<std::string, 32> &Headers,772        bool DoBlockCheckHeaderListOnly)773      : BlockCheckHeaderListOnly(DoBlockCheckHeaderListOnly),774        CurrentInclusionPathHandle(InclusionPathHandleInvalid),775        InNestedHeader(false) {776    // Use canonical header path representation.777    for (llvm::ArrayRef<std::string>::iterator I = Headers.begin(),778      E = Headers.end();779      I != E; ++I) {780      HeaderList.push_back(getCanonicalPath(*I));781    }782  }783 784  ~PreprocessorTrackerImpl() override {}785 786  // Handle entering a preprocessing session.787  void handlePreprocessorEntry(clang::Preprocessor &PP,788                               llvm::StringRef rootHeaderFile) override {789    HeadersInThisCompile.clear();790    assert((HeaderStack.size() == 0) && "Header stack should be empty.");791    pushHeaderHandle(addHeader(rootHeaderFile));792    PP.addPPCallbacks(std::make_unique<PreprocessorCallbacks>(*this, PP,793                                                               rootHeaderFile));794  }795  // Handle exiting a preprocessing session.796  void handlePreprocessorExit() override { HeaderStack.clear(); }797 798  // Handle include directive.799  // This function is called every time an include directive is seen by the800  // preprocessor, for the purpose of later checking for 'extern "" {}' or801  // "namespace {}" blocks containing #include directives.802  void handleIncludeDirective(llvm::StringRef DirectivePath, int DirectiveLine,803                              int DirectiveColumn,804                              llvm::StringRef TargetPath) override {805    // If it's not a header in the header list, ignore it with respect to806    // the check.807    if (BlockCheckHeaderListOnly && !isHeaderListHeader(TargetPath))808      return;809    HeaderHandle CurrentHeaderHandle = findHeaderHandle(DirectivePath);810    StringHandle IncludeHeaderHandle = addString(TargetPath);811    for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),812                                                E = IncludeDirectives.end();813         I != E; ++I) {814      // If we already have an entry for this directive, return now.815      if ((I->File == CurrentHeaderHandle) && (I->Line == DirectiveLine))816        return;817    }818    PPItemKey IncludeDirectiveItem(IncludeHeaderHandle, CurrentHeaderHandle,819                                   DirectiveLine, DirectiveColumn);820    IncludeDirectives.push_back(IncludeDirectiveItem);821  }822 823  // Check for include directives within the given source line range.824  // Report errors if any found.  Returns true if no include directives825  // found in block.826  bool checkForIncludesInBlock(clang::Preprocessor &PP,827                               clang::SourceRange BlockSourceRange,828                               const char *BlockIdentifierMessage,829                               llvm::raw_ostream &OS) override {830    clang::SourceLocation BlockStartLoc = BlockSourceRange.getBegin();831    clang::SourceLocation BlockEndLoc = BlockSourceRange.getEnd();832    // Use block location to get FileID of both the include directive833    // and block statement.834    clang::FileID FileID = PP.getSourceManager().getFileID(BlockStartLoc);835    std::string SourcePath = getSourceLocationFile(PP, BlockStartLoc);836    SourcePath = ModularizeUtilities::getCanonicalPath(SourcePath);837    HeaderHandle SourceHandle = findHeaderHandle(SourcePath);838    if (SourceHandle == -1)839      return true;840    int BlockStartLine, BlockStartColumn, BlockEndLine, BlockEndColumn;841    bool returnValue = true;842    getSourceLocationLineAndColumn(PP, BlockStartLoc, BlockStartLine,843                                   BlockStartColumn);844    getSourceLocationLineAndColumn(PP, BlockEndLoc, BlockEndLine,845                                   BlockEndColumn);846    for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),847                                                E = IncludeDirectives.end();848         I != E; ++I) {849      // If we find an entry within the block, report an error.850      if ((I->File == SourceHandle) && (I->Line >= BlockStartLine) &&851          (I->Line < BlockEndLine)) {852        returnValue = false;853        OS << SourcePath << ":" << I->Line << ":" << I->Column << ":\n";854        OS << getSourceLine(PP, FileID, I->Line) << "\n";855        if (I->Column > 0)856          OS << std::string(I->Column - 1, ' ') << "^\n";857        OS << "error: Include directive within " << BlockIdentifierMessage858           << ".\n";859        OS << SourcePath << ":" << BlockStartLine << ":" << BlockStartColumn860           << ":\n";861        OS << getSourceLine(PP, BlockStartLoc) << "\n";862        if (BlockStartColumn > 0)863          OS << std::string(BlockStartColumn - 1, ' ') << "^\n";864        OS << "The \"" << BlockIdentifierMessage << "\" block is here.\n";865      }866    }867    return returnValue;868  }869 870  // Handle entering a header source file.871  void handleHeaderEntry(clang::Preprocessor &PP, llvm::StringRef HeaderPath) {872    // Ignore <built-in> and <command-line> to reduce message clutter.873    if (HeaderPath.starts_with("<"))874      return;875    HeaderHandle H = addHeader(HeaderPath);876    if (H != getCurrentHeaderHandle())877      pushHeaderHandle(H);878    // Check for nested header.879    if (!InNestedHeader)880      InNestedHeader = !HeadersInThisCompile.insert(H).second;881  }882 883  // Handle exiting a header source file.884  void handleHeaderExit(llvm::StringRef HeaderPath) {885    // Ignore <built-in> and <command-line> to reduce message clutter.886    if (HeaderPath.starts_with("<"))887      return;888    HeaderHandle H = findHeaderHandle(HeaderPath);889    HeaderHandle TH;890    if (isHeaderHandleInStack(H)) {891      do {892        TH = getCurrentHeaderHandle();893        popHeaderHandle();894      } while ((TH != H) && (HeaderStack.size() != 0));895    }896    InNestedHeader = false;897  }898 899  // Lookup/add string.900  StringHandle addString(llvm::StringRef Str) {901    return Strings.insert(Str).first->first();902  }903 904  // Convert to a canonical path.905  std::string getCanonicalPath(llvm::StringRef path) const {906    std::string CanonicalPath(path);907    llvm::replace(CanonicalPath, '\\', '/');908    return CanonicalPath;909  }910 911  // Return true if the given header is in the header list.912  bool isHeaderListHeader(llvm::StringRef HeaderPath) const {913    std::string CanonicalPath = getCanonicalPath(HeaderPath);914    for (llvm::ArrayRef<std::string>::iterator I = HeaderList.begin(),915        E = HeaderList.end();916        I != E; ++I) {917      if (*I == CanonicalPath)918        return true;919    }920    return false;921  }922 923  // Get the handle of a header file entry.924  // Return HeaderHandleInvalid if not found.925  HeaderHandle findHeaderHandle(llvm::StringRef HeaderPath) const {926    std::string CanonicalPath = getCanonicalPath(HeaderPath);927    HeaderHandle H = 0;928    for (auto I = HeaderPaths.begin(), E = HeaderPaths.end(); I != E;929         ++I, ++H) {930      if (*I == CanonicalPath)931        return H;932    }933    return HeaderHandleInvalid;934  }935 936  // Add a new header file entry, or return existing handle.937  // Return the header handle.938  HeaderHandle addHeader(llvm::StringRef HeaderPath) {939    std::string CanonicalPath = getCanonicalPath(HeaderPath);940    HeaderHandle H = findHeaderHandle(CanonicalPath);941    if (H == HeaderHandleInvalid) {942      H = HeaderPaths.size();943      HeaderPaths.push_back(addString(CanonicalPath));944    }945    return H;946  }947 948  // Return a header file path string given its handle.949  StringHandle getHeaderFilePath(HeaderHandle H) const {950    if ((H >= 0) && (H < (HeaderHandle)HeaderPaths.size()))951      return HeaderPaths[H];952    return StringHandle();953  }954 955  // Returns a handle to the inclusion path.956  InclusionPathHandle pushHeaderHandle(HeaderHandle H) {957    HeaderStack.push_back(H);958    return CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack);959  }960  // Pops the last header handle from the stack;961  void popHeaderHandle() {962    // assert((HeaderStack.size() != 0) && "Header stack already empty.");963    if (HeaderStack.size() != 0) {964      HeaderStack.pop_back();965      CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack);966    }967  }968  // Get the top handle on the header stack.969  HeaderHandle getCurrentHeaderHandle() const {970    if (HeaderStack.size() != 0)971      return HeaderStack.back();972    return HeaderHandleInvalid;973  }974 975  // Check for presence of header handle in the header stack.976  bool isHeaderHandleInStack(HeaderHandle H) const {977    return llvm::is_contained(HeaderStack, H);978  }979 980  // Get the handle of a header inclusion path entry.981  // Return InclusionPathHandleInvalid if not found.982  InclusionPathHandle983  findInclusionPathHandle(const std::vector<HeaderHandle> &Path) const {984    InclusionPathHandle H = 0;985    for (auto I = InclusionPaths.begin(), E = InclusionPaths.end(); I != E;986         ++I, ++H) {987      if (I->Path == Path)988        return H;989    }990    return HeaderHandleInvalid;991  }992  // Add a new header inclusion path entry, or return existing handle.993  // Return the header inclusion path entry handle.994  InclusionPathHandle995  addInclusionPathHandle(const std::vector<HeaderHandle> &Path) {996    InclusionPathHandle H = findInclusionPathHandle(Path);997    if (H == HeaderHandleInvalid) {998      H = InclusionPaths.size();999      InclusionPaths.push_back(HeaderInclusionPath(Path));1000    }1001    return H;1002  }1003  // Return the current inclusion path handle.1004  InclusionPathHandle getCurrentInclusionPathHandle() const {1005    return CurrentInclusionPathHandle;1006  }1007 1008  // Return an inclusion path given its handle.1009  const std::vector<HeaderHandle> &1010  getInclusionPath(InclusionPathHandle H) const {1011    if ((H >= 0) && (H <= (InclusionPathHandle)InclusionPaths.size()))1012      return InclusionPaths[H].Path;1013    static std::vector<HeaderHandle> Empty;1014    return Empty;1015  }1016 1017  // Add a macro expansion instance.1018  void addMacroExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,1019                                 clang::SourceLocation InstanceLoc,1020                                 clang::SourceLocation DefinitionLoc,1021                                 clang::IdentifierInfo *II,1022                                 llvm::StringRef MacroUnexpanded,1023                                 llvm::StringRef MacroExpanded,1024                                 InclusionPathHandle InclusionPathHandle) {1025    if (InNestedHeader)1026      return;1027    StringHandle MacroName = addString(II->getName());1028    PPItemKey InstanceKey(PP, MacroName, H, InstanceLoc);1029    PPItemKey DefinitionKey(PP, MacroName, H, DefinitionLoc);1030    auto I = MacroExpansions.find(InstanceKey);1031    // If existing instance of expansion not found, add one.1032    if (I == MacroExpansions.end()) {1033      std::string InstanceSourceLine =1034          getSourceLocationString(PP, InstanceLoc) + ":\n" +1035          getSourceLine(PP, InstanceLoc) + "\n";1036      std::string DefinitionSourceLine =1037          getSourceLocationString(PP, DefinitionLoc) + ":\n" +1038          getSourceLine(PP, DefinitionLoc) + "\n";1039      MacroExpansions[InstanceKey] = MacroExpansionTracker(1040          addString(MacroUnexpanded), addString(MacroExpanded),1041          addString(InstanceSourceLine), DefinitionKey,1042          addString(DefinitionSourceLine), InclusionPathHandle);1043    } else {1044      // We've seen the macro before.  Get its tracker.1045      MacroExpansionTracker &CondTracker = I->second;1046      // Look up an existing instance value for the macro.1047      MacroExpansionInstance *MacroInfo =1048          CondTracker.findMacroExpansionInstance(addString(MacroExpanded),1049                                                 DefinitionKey);1050      // If found, just add the inclusion path to the instance.1051      if (MacroInfo)1052        MacroInfo->addInclusionPathHandle(InclusionPathHandle);1053      else {1054        // Otherwise add a new instance with the unique value.1055        std::string DefinitionSourceLine =1056            getSourceLocationString(PP, DefinitionLoc) + ":\n" +1057            getSourceLine(PP, DefinitionLoc) + "\n";1058        CondTracker.addMacroExpansionInstance(1059            addString(MacroExpanded), DefinitionKey,1060            addString(DefinitionSourceLine), InclusionPathHandle);1061      }1062    }1063  }1064 1065  // Add a conditional expansion instance.1066  void1067  addConditionalExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,1068                                  clang::SourceLocation InstanceLoc,1069                                  clang::tok::PPKeywordKind DirectiveKind,1070                                  clang::PPCallbacks::ConditionValueKind ConditionValue,1071                                  llvm::StringRef ConditionUnexpanded,1072                                  InclusionPathHandle InclusionPathHandle) {1073    // Ignore header guards, assuming the header guard is the only conditional.1074    if (InNestedHeader)1075      return;1076    StringHandle ConditionUnexpandedHandle(addString(ConditionUnexpanded));1077    PPItemKey InstanceKey(PP, ConditionUnexpandedHandle, H, InstanceLoc);1078    auto I = ConditionalExpansions.find(InstanceKey);1079    // If existing instance of condition not found, add one.1080    if (I == ConditionalExpansions.end()) {1081      std::string InstanceSourceLine =1082          getSourceLocationString(PP, InstanceLoc) + ":\n" +1083          getSourceLine(PP, InstanceLoc) + "\n";1084      ConditionalExpansions[InstanceKey] =1085          ConditionalTracker(DirectiveKind, ConditionValue,1086                             ConditionUnexpandedHandle, InclusionPathHandle);1087    } else {1088      // We've seen the conditional before.  Get its tracker.1089      ConditionalTracker &CondTracker = I->second;1090      // Look up an existing instance value for the condition.1091      ConditionalExpansionInstance *MacroInfo =1092          CondTracker.findConditionalExpansionInstance(ConditionValue);1093      // If found, just add the inclusion path to the instance.1094      if (MacroInfo)1095        MacroInfo->addInclusionPathHandle(InclusionPathHandle);1096      else {1097        // Otherwise add a new instance with the unique value.1098        CondTracker.addConditionalExpansionInstance(ConditionValue,1099                                                    InclusionPathHandle);1100      }1101    }1102  }1103 1104  // Report on inconsistent macro instances.1105  // Returns true if any mismatches.1106  bool reportInconsistentMacros(llvm::raw_ostream &OS) override {1107    bool ReturnValue = false;1108    // Walk all the macro expansion trackers in the map.1109    for (auto I = MacroExpansions.begin(), E = MacroExpansions.end(); I != E;1110         ++I) {1111      const PPItemKey &ItemKey = I->first;1112      MacroExpansionTracker &MacroExpTracker = I->second;1113      // If no mismatch (only one instance value) continue.1114      if (!MacroExpTracker.hasMismatch())1115        continue;1116      // Tell caller we found one or more errors.1117      ReturnValue = true;1118      // Start the error message.1119      OS << MacroExpTracker.InstanceSourceLine;1120      if (ItemKey.Column > 0)1121        OS << std::string(ItemKey.Column - 1, ' ') << "^\n";1122      OS << "error: Macro instance '" << MacroExpTracker.MacroUnexpanded1123         << "' has different values in this header, depending on how it was "1124            "included.\n";1125      // Walk all the instances.1126      for (auto IMT = MacroExpTracker.MacroExpansionInstances.begin(),1127                EMT = MacroExpTracker.MacroExpansionInstances.end();1128           IMT != EMT; ++IMT) {1129        MacroExpansionInstance &MacroInfo = *IMT;1130        OS << "  '" << MacroExpTracker.MacroUnexpanded << "' expanded to: '"1131           << MacroInfo.MacroExpanded1132           << "' with respect to these inclusion paths:\n";1133        // Walk all the inclusion path hierarchies.1134        for (auto IIP = MacroInfo.InclusionPathHandles.begin(),1135                  EIP = MacroInfo.InclusionPathHandles.end();1136             IIP != EIP; ++IIP) {1137          const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);1138          auto Count = (int)ip.size();1139          for (int Index = 0; Index < Count; ++Index) {1140            HeaderHandle H = ip[Index];1141            OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H)1142               << "\n";1143          }1144        }1145        // For a macro that wasn't defined, we flag it by using the1146        // instance location.1147        // If there is a definition...1148        if (MacroInfo.DefinitionLocation.Line != ItemKey.Line) {1149          OS << MacroInfo.DefinitionSourceLine;1150          if (MacroInfo.DefinitionLocation.Column > 0)1151            OS << std::string(MacroInfo.DefinitionLocation.Column - 1, ' ')1152               << "^\n";1153          OS << "Macro defined here.\n";1154        } else1155          OS << "(no macro definition)"1156             << "\n";1157      }1158    }1159    return ReturnValue;1160  }1161 1162  // Report on inconsistent conditional instances.1163  // Returns true if any mismatches.1164  bool reportInconsistentConditionals(llvm::raw_ostream &OS) override {1165    bool ReturnValue = false;1166    // Walk all the conditional trackers in the map.1167    for (auto I = ConditionalExpansions.begin(),1168              E = ConditionalExpansions.end();1169         I != E; ++I) {1170      const PPItemKey &ItemKey = I->first;1171      ConditionalTracker &CondTracker = I->second;1172      if (!CondTracker.hasMismatch())1173        continue;1174      // Tell caller we found one or more errors.1175      ReturnValue = true;1176      // Start the error message.1177      OS << HeaderPaths[ItemKey.File] << ":" << ItemKey.Line << ":"1178         << ItemKey.Column << "\n";1179      OS << "#" << getDirectiveSpelling(CondTracker.DirectiveKind) << " "1180         << CondTracker.ConditionUnexpanded << "\n";1181      OS << "^\n";1182      OS << "error: Conditional expression instance '"1183         << CondTracker.ConditionUnexpanded1184         << "' has different values in this header, depending on how it was "1185            "included.\n";1186      // Walk all the instances.1187      for (auto IMT = CondTracker.ConditionalExpansionInstances.begin(),1188                EMT = CondTracker.ConditionalExpansionInstances.end();1189           IMT != EMT; ++IMT) {1190        ConditionalExpansionInstance &MacroInfo = *IMT;1191        OS << "  '" << CondTracker.ConditionUnexpanded << "' expanded to: '"1192           << ConditionValueKindStrings[MacroInfo.ConditionValue]1193           << "' with respect to these inclusion paths:\n";1194        // Walk all the inclusion path hierarchies.1195        for (auto IIP = MacroInfo.InclusionPathHandles.begin(),1196                  EIP = MacroInfo.InclusionPathHandles.end();1197             IIP != EIP; ++IIP) {1198          const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);1199          auto Count = (int)ip.size();1200          for (int Index = 0; Index < Count; ++Index) {1201            HeaderHandle H = ip[Index];1202            OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H)1203               << "\n";1204          }1205        }1206      }1207    }1208    return ReturnValue;1209  }1210 1211  // Get directive spelling.1212  static const char *getDirectiveSpelling(clang::tok::PPKeywordKind kind) {1213    switch (kind) {1214    case clang::tok::pp_if:1215      return "if";1216    case clang::tok::pp_elif:1217      return "elif";1218    case clang::tok::pp_ifdef:1219      return "ifdef";1220    case clang::tok::pp_ifndef:1221      return "ifndef";1222    default:1223      return "(unknown)";1224    }1225  }1226 1227private:1228  llvm::SmallVector<std::string, 32> HeaderList;1229  // Only do extern, namespace check for headers in HeaderList.1230  bool BlockCheckHeaderListOnly;1231  llvm::StringSet<> Strings;1232  std::vector<StringHandle> HeaderPaths;1233  std::vector<HeaderHandle> HeaderStack;1234  std::vector<HeaderInclusionPath> InclusionPaths;1235  InclusionPathHandle CurrentInclusionPathHandle;1236  llvm::SmallSet<HeaderHandle, 32> HeadersInThisCompile;1237  std::vector<PPItemKey> IncludeDirectives;1238  MacroExpansionMap MacroExpansions;1239  ConditionalExpansionMap ConditionalExpansions;1240  bool InNestedHeader;1241};1242 1243} // namespace1244 1245// PreprocessorTracker functions.1246 1247// PreprocessorTracker destructor.1248PreprocessorTracker::~PreprocessorTracker() {}1249 1250// Create instance of PreprocessorTracker.1251PreprocessorTracker *PreprocessorTracker::create(1252    llvm::SmallVector<std::string, 32> &Headers,1253    bool DoBlockCheckHeaderListOnly) {1254  return new PreprocessorTrackerImpl(Headers, DoBlockCheckHeaderListOnly);1255}1256 1257// Preprocessor callbacks for modularize.1258 1259// Handle include directive.1260void PreprocessorCallbacks::InclusionDirective(1261    clang::SourceLocation HashLoc, const clang::Token &IncludeTok,1262    llvm::StringRef FileName, bool IsAngled,1263    clang::CharSourceRange FilenameRange, clang::OptionalFileEntryRef File,1264    llvm::StringRef SearchPath, llvm::StringRef RelativePath,1265    const clang::Module *SuggestedModule, bool ModuleImported,1266    clang::SrcMgr::CharacteristicKind FileType) {1267  int DirectiveLine, DirectiveColumn;1268  std::string HeaderPath = getSourceLocationFile(PP, HashLoc);1269  getSourceLocationLineAndColumn(PP, HashLoc, DirectiveLine, DirectiveColumn);1270  PPTracker.handleIncludeDirective(HeaderPath, DirectiveLine, DirectiveColumn,1271                                   FileName);1272}1273 1274// Handle file entry/exit.1275void PreprocessorCallbacks::FileChanged(1276    clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason,1277    clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) {1278  switch (Reason) {1279  case EnterFile:1280    PPTracker.handleHeaderEntry(PP, getSourceLocationFile(PP, Loc));1281    break;1282  case ExitFile: {1283    clang::OptionalFileEntryRef F =1284        PP.getSourceManager().getFileEntryRefForID(PrevFID);1285    if (F)1286      PPTracker.handleHeaderExit(F->getName());1287  } break;1288  case SystemHeaderPragma:1289  case RenameFile:1290    break;1291  }1292}1293 1294// Handle macro expansion.1295void PreprocessorCallbacks::MacroExpands(const clang::Token &MacroNameTok,1296                                         const clang::MacroDefinition &MD,1297                                         clang::SourceRange Range,1298                                         const clang::MacroArgs *Args) {1299  clang::SourceLocation Loc = Range.getBegin();1300  // Ignore macro argument expansions.1301  if (!Loc.isFileID())1302    return;1303  clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();1304  const clang::MacroInfo *MI = MD.getMacroInfo();1305  std::string MacroName = II->getName().str();1306  std::string Unexpanded(getMacroUnexpandedString(Range, PP, MacroName, MI));1307  std::string Expanded(getMacroExpandedString(PP, MacroName, MI, Args));1308  PPTracker.addMacroExpansionInstance(1309      PP, PPTracker.getCurrentHeaderHandle(), Loc, MI->getDefinitionLoc(), II,1310      Unexpanded, Expanded, PPTracker.getCurrentInclusionPathHandle());1311}1312 1313void PreprocessorCallbacks::Defined(const clang::Token &MacroNameTok,1314                                    const clang::MacroDefinition &MD,1315                                    clang::SourceRange Range) {1316  clang::SourceLocation Loc(Range.getBegin());1317  clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();1318  const clang::MacroInfo *MI = MD.getMacroInfo();1319  std::string Unexpanded(getSourceString(PP, Range));1320  PPTracker.addMacroExpansionInstance(1321      PP, PPTracker.getCurrentHeaderHandle(), Loc,1322      (MI ? MI->getDefinitionLoc() : Loc), II, Unexpanded,1323      (MI ? "true" : "false"), PPTracker.getCurrentInclusionPathHandle());1324}1325 1326void PreprocessorCallbacks::If(clang::SourceLocation Loc,1327                               clang::SourceRange ConditionRange,1328                               clang::PPCallbacks::ConditionValueKind ConditionResult) {1329  std::string Unexpanded(getSourceString(PP, ConditionRange));1330  PPTracker.addConditionalExpansionInstance(1331      PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_if,1332      ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());1333}1334 1335void PreprocessorCallbacks::Elif(clang::SourceLocation Loc,1336                                 clang::SourceRange ConditionRange,1337                                 clang::PPCallbacks::ConditionValueKind ConditionResult,1338                                 clang::SourceLocation IfLoc) {1339  std::string Unexpanded(getSourceString(PP, ConditionRange));1340  PPTracker.addConditionalExpansionInstance(1341      PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_elif,1342      ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());1343}1344 1345void PreprocessorCallbacks::Ifdef(clang::SourceLocation Loc,1346                                  const clang::Token &MacroNameTok,1347                                  const clang::MacroDefinition &MD) {1348  clang::PPCallbacks::ConditionValueKind IsDefined =1349    (MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );1350  PPTracker.addConditionalExpansionInstance(1351      PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifdef,1352      IsDefined, PP.getSpelling(MacroNameTok),1353      PPTracker.getCurrentInclusionPathHandle());1354}1355 1356void PreprocessorCallbacks::Ifndef(clang::SourceLocation Loc,1357                                   const clang::Token &MacroNameTok,1358                                   const clang::MacroDefinition &MD) {1359  clang::PPCallbacks::ConditionValueKind IsNotDefined =1360    (!MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );1361  PPTracker.addConditionalExpansionInstance(1362      PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifndef,1363      IsNotDefined, PP.getSpelling(MacroNameTok),1364      PPTracker.getCurrentInclusionPathHandle());1365}1366} // end namespace Modularize1367