xref: /llvm-project/clang-tools-extra/modularize/PreprocessorTracker.cpp (revision da95d926f6fce4ed9707c77908ad96624268f134)
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-exception
6 //
7 //===--------------------------------------------------------------------===//
8 //
9 // The Basic Idea (Macro and Conditional Checking)
10 //
11 // Basically we install a PPCallbacks-derived object to track preprocessor
12 // activity, namely when a header file is entered/exited, when a macro
13 // 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 of
18 // 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 macro
20 // or "defined" expression evaluates to the same value, which is the
21 // desired case, only one state is stored.  Similarly, for conditional
22 // directives, we save the condition expression states in a separate map.
23 //
24 // This information is collected as modularize compiles all the headers
25 // given to it to process.  After all the compilations are performed,
26 // a check is performed for any entries in the maps that contain more
27 // 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 == 1
33 //       ^
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.h
38 //         (...)/SubHeader.h
39 //   (...)/SubHeader.h:3:9:
40 //   #define SYMBOL 1
41 //             ^
42 //   Macro defined here.
43 //     'SYMBOL' expanded to: '2' with respect to these inclusion paths:
44 //       (...)/Header2.h
45 //           (...)/SubHeader.h
46 //   (...)/SubHeader.h:7:9:
47 //   #define SYMBOL 2
48 //             ^
49 //   Macro defined here.
50 //
51 // The Basic Idea ('Extern "C/C++" {}' Or 'namespace {}') With Nested
52 // '#include' Checking)
53 //
54 // To check for '#include' directives nested inside 'Extern "C/C++" {}'
55 // or 'namespace {}' blocks, we keep track of the '#include' directives
56 // while running the preprocessor, and later during a walk of the AST
57 // we call a function to check for any '#include' directives inside
58 // an 'Extern "C/C++" {}' or 'namespace {}' block, given its source
59 // range.
60 //
61 // Design and Implementation Details (Macro and Conditional Checking)
62 //
63 // A PreprocessorTrackerImpl class implements the PreprocessorTracker
64 // interface. It uses a PreprocessorCallbacks class derived from PPCallbacks
65 // to track preprocessor activity, namely entering/exiting a header, macro
66 // expansions, use of "defined" expressions, and #if, #elif, #ifdef, and
67 // #ifndef conditional directives. PreprocessorTrackerImpl stores a map
68 // of MacroExpansionTracker objects keyed on a name/file/line/column
69 // value represented by a light-weight PPItemKey value object. This
70 // is the key top-level data structure tracking the values of macro
71 // expansion instances.  Similarly, it stores a map of ConditionalTracker
72 // objects with the same kind of key, for tracking preprocessor conditional
73 // directives.
74 //
75 // The MacroExpansionTracker object represents one macro reference or use
76 // of a "defined" expression in a header file. It stores a handle to a
77 // string representing the unexpanded macro instance, a handle to a string
78 // representing the unpreprocessed source line containing the unexpanded
79 // macro instance, and a vector of one or more MacroExpansionInstance
80 // objects.
81 //
82 // The MacroExpansionInstance object represents one or more expansions
83 // of a macro reference, for the case where the macro expands to the same
84 // value. MacroExpansionInstance stores a handle to a string representing
85 // the expanded macro value, a PPItemKey representing the file/line/column
86 // where the macro was defined, a handle to a string representing the source
87 // line containing the macro definition, and a vector of InclusionPathHandle
88 // values that represents the hierarchies of include files for each case
89 // where the particular header containing the macro reference was referenced
90 // or included.
91 
92 // In the normal case where a macro instance always expands to the same
93 // value, the MacroExpansionTracker object will only contain one
94 // MacroExpansionInstance representing all the macro expansion instances.
95 // If a case was encountered where a macro instance expands to a value
96 // that is different from that seen before, or the macro was defined in
97 // a different place, a new MacroExpansionInstance object representing
98 // that case will be added to the vector in MacroExpansionTracker. If a
99 // macro instance expands to a value already seen before, the
100 // InclusionPathHandle representing that case's include file hierarchy
101 // will be added to the existing MacroExpansionInstance object.
102 
103 // For checking conditional directives, the ConditionalTracker class
104 // functions similarly to MacroExpansionTracker, but tracks an #if,
105 // #elif, #ifdef, or #ifndef directive in a header file.  It stores
106 // a vector of one or two ConditionalExpansionInstance objects,
107 // representing the cases where the conditional expression evaluates
108 // to true or false.  This latter object stores the evaluated value
109 // of the condition expression (a bool) and a vector of
110 // InclusionPathHandles.
111 //
112 // To reduce the instances of string and object copying, the
113 // PreprocessorTrackerImpl class uses a StringPool to save all stored
114 // strings, and defines a StringHandle type to abstract the references
115 // to the strings.
116 //
117 // PreprocessorTrackerImpl also maintains a list representing the unique
118 // headers, which is just a vector of StringHandle's for the header file
119 // paths. A HeaderHandle abstracts a reference to a header, and is simply
120 // the index of the stored header file path.
121 //
122 // A HeaderInclusionPath class abstracts a unique hierarchy of header file
123 // inclusions. It simply stores a vector of HeaderHandles ordered from the
124 // top-most header (the one from the header list passed to modularize) down
125 // to the header containing the macro reference. PreprocessorTrackerImpl
126 // stores a vector of these objects. An InclusionPathHandle typedef
127 // abstracts a reference to one of the HeaderInclusionPath objects, and is
128 // simply the index of the stored HeaderInclusionPath object. The
129 // MacroExpansionInstance object stores a vector of these handles so that
130 // the reporting function can display the include hierarchies for the macro
131 // expansion instances represented by that object, to help the user
132 // understand how the header was included. (A future enhancement might
133 // be to associate a line number for the #include directives, but I
134 // 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 the
137 // internal objects light-weight value objects, in order to reduce string
138 // and object copying overhead, and to abstract this implementation detail.
139 //
140 // The key data structures are built up while modularize runs the headers
141 // through the compilation. A PreprocessorTracker instance is created and
142 // passed down to the AST action and consumer objects in modularize. For
143 // each new compilation instance, the consumer calls the
144 // PreprocessorTracker's handleNewPreprocessorEntry function, which sets
145 // up a PreprocessorCallbacks object for the preprocessor. At the end of
146 // the compilation instance, the PreprocessorTracker's
147 // handleNewPreprocessorExit function handles cleaning up with respect
148 // to the preprocessing instance.
149 //
150 // The PreprocessorCallbacks object uses an overridden FileChanged callback
151 // to determine when a header is entered and exited (including exiting the
152 // header during #include directives). It calls PreprocessorTracker's
153 // handleHeaderEntry and handleHeaderExit functions upon entering and
154 // exiting a header. These functions manage a stack of header handles
155 // representing by a vector, pushing and popping header handles as headers
156 // 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 callback
160 // to track when a macro expansion is performed. It calls a couple of helper
161 // functions to get the unexpanded and expanded macro values as strings, but
162 // then calls PreprocessorTrackerImpl's addMacroExpansionInstance function to
163 // do the rest of the work. The getMacroExpandedString function uses the
164 // preprocessor's getSpelling to convert tokens to strings using the
165 // information passed to the MacroExpands callback, and simply concatenates
166 // them. It makes recursive calls to itself to handle nested macro
167 // definitions, and also handles function-style macros.
168 //
169 // PreprocessorTrackerImpl's addMacroExpansionInstance function looks for
170 // an existing MacroExpansionTracker entry in its map of MacroExampleTracker
171 // objects. If none exists, it adds one with one MacroExpansionInstance and
172 // returns. If a MacroExpansionTracker object already exists, it looks for
173 // an existing MacroExpansionInstance object stored in the
174 // MacroExpansionTracker object, one that matches the macro expanded value
175 // and the macro definition location. If a matching MacroExpansionInstance
176 // object is found, it just adds the current HeaderInclusionPath object to
177 // it. If not found, it creates and stores a new MacroExpansionInstance
178 // object. The addMacroExpansionInstance function calls a couple of helper
179 // functions to get the pre-formatted location and source line strings for
180 // the macro reference and the macro definition stored as string handles.
181 // These helper functions use the current source manager from the
182 // preprocessor. This is done in advance at this point in time because the
183 // source manager doesn't exist at the time of the reporting.
184 //
185 // For conditional check, the PreprocessorCallbacks class overrides the
186 // PPCallbacks handlers for #if, #elif, #ifdef, and #ifndef.  These handlers
187 // call the addConditionalExpansionInstance method of
188 // PreprocessorTrackerImpl.  The process is similar to that of macros, but
189 // with some different data and error messages.  A lookup is performed for
190 // the conditional, and if a ConditionalTracker object doesn't yet exist for
191 // the conditional, a new one is added, including adding a
192 // ConditionalExpansionInstance object to it to represent the condition
193 // expression state.  If a ConditionalTracker for the conditional does
194 // exist, a lookup is made for a ConditionalExpansionInstance object
195 // matching the condition expression state.  If one exists, a
196 // HeaderInclusionPath is added to it.  Otherwise a new
197 // ConditionalExpansionInstance  entry is made.  If a ConditionalTracker
198 // has two ConditionalExpansionInstance objects, it means there was a
199 // conflict, meaning the conditional expression evaluated differently in
200 // one or more cases.
201 //
202 // After modularize has performed all the compilations, it enters a phase
203 // of error reporting. This new feature adds to this reporting phase calls
204 // to the PreprocessorTracker's reportInconsistentMacros and
205 // reportInconsistentConditionals functions. These functions walk the maps
206 // of MacroExpansionTracker's and ConditionalTracker's respectively. If
207 // any of these objects have more than one MacroExpansionInstance or
208 // ConditionalExpansionInstance objects, it formats and outputs an error
209 // message like the example shown previously, using the stored data.
210 //
211 // A potential issue is that there is some overlap between the #if/#elif
212 // 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 output
215 // message marking the phases.
216 //
217 // Design and Implementation Details ('Extern "C/C++" {}' Or
218 // 'namespace {}') With Nested '#include' Checking)
219 //
220 // We override the InclusionDirective in PPCallbacks to record information
221 // about each '#include' directive encountered during preprocessing.
222 // We co-opt the PPItemKey class to store the information about each
223 // '#include' directive, including the source file name containing the
224 // directive, the name of the file being included, and the source line
225 // 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 provide
229 // visit handlers for 'extern "C/C++" {}' and 'namespace (name) {}'
230 // blocks, checking to see if any '#include' directives occurred
231 // within the blocks, reporting errors if any found.
232 //
233 // Future Directions
234 //
235 // We probably should add options to disable any of the checks, in case
236 // 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 messages
240 // (the second part referring to definitions being different), attempting
241 // to tie them to the last macro conflict encountered with respect to the
242 // 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 
255 namespace Modularize {
256 
257 // Some handle types
258 typedef llvm::StringRef StringHandle;
259 
260 typedef int HeaderHandle;
261 const HeaderHandle HeaderHandleInvalid = -1;
262 
263 typedef int InclusionPathHandle;
264 const InclusionPathHandle InclusionPathHandleInvalid = -1;
265 
266 // Some utility functions.
267 
268 // Get a "file:line:column" source location string.
getSourceLocationString(clang::Preprocessor & PP,clang::SourceLocation Loc)269 static std::string getSourceLocationString(clang::Preprocessor &PP,
270                                            clang::SourceLocation Loc) {
271   if (Loc.isInvalid())
272     return std::string("(none)");
273   else
274     return Loc.printToString(PP.getSourceManager());
275 }
276 
277 // Get just the file name from a source location.
getSourceLocationFile(clang::Preprocessor & PP,clang::SourceLocation Loc)278 static 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.
getSourceLocationLineAndColumn(clang::Preprocessor & PP,clang::SourceLocation Loc,int & Line,int & Column)288 static 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.
getSourceString(clang::Preprocessor & PP,clang::SourceRange Range)302 static 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.
getSourceLine(clang::Preprocessor & PP,clang::SourceLocation Loc)313 static 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.
getSourceLine(clang::Preprocessor & PP,clang::FileID FileID,int Line)339 static 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 token
374 // for the macro instance, which in the case of a function-style
375 // macro will be a ')', but for an object-style macro, it
376 // will be the macro name itself.
getMacroUnexpandedString(clang::SourceRange Range,clang::Preprocessor & PP,llvm::StringRef MacroName,const clang::MacroInfo * MI)377 static 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   std::string Unexpanded;
385   if (MI->isFunctionLike()) {
386     clang::SourceLocation EndLoc(Range.getEnd());
387     const char *EndPtr = PP.getSourceManager().getCharacterData(EndLoc) + 1;
388     Length = (EndPtr - BeginPtr) + 1; // +1 is ')' width.
389   } else
390     Length = MacroName.size();
391   return llvm::StringRef(BeginPtr, Length).trim().str();
392 }
393 
394 // Get the expansion for a macro instance, given the information
395 // provided by PPCallbacks.
396 // FIXME: This doesn't support function-style macro instances
397 // passed as arguments to another function-style macro. However,
398 // since it still expands the inner arguments, it still
399 // allows modularize to effectively work with respect to macro
400 // consistency checking, although it displays the incorrect
401 // expansion in error messages.
getMacroExpandedString(clang::Preprocessor & PP,llvm::StringRef MacroName,const clang::MacroInfo * MI,const clang::MacroArgs * Args)402 static std::string getMacroExpandedString(clang::Preprocessor &PP,
403                                           llvm::StringRef MacroName,
404                                           const clang::MacroInfo *MI,
405                                           const clang::MacroArgs *Args) {
406   std::string Expanded;
407   // Walk over the macro Tokens.
408   for (const auto &T : MI->tokens()) {
409     clang::IdentifierInfo *II = T.getIdentifierInfo();
410     int ArgNo = (II && Args ? MI->getParameterNum(II) : -1);
411     if (ArgNo == -1) {
412       // This isn't an argument, just add it.
413       if (II == nullptr)
414         Expanded += PP.getSpelling(T); // Not an identifier.
415       else {
416         // Token is for an identifier.
417         std::string Name = II->getName().str();
418         // Check for nexted macro references.
419         clang::MacroInfo *MacroInfo = PP.getMacroInfo(II);
420         if (MacroInfo && (Name != MacroName))
421           Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr);
422         else
423           Expanded += Name;
424       }
425       continue;
426     }
427     // We get here if it's a function-style macro with arguments.
428     const clang::Token *ResultArgToks;
429     const clang::Token *ArgTok = Args->getUnexpArgument(ArgNo);
430     if (Args->ArgNeedsPreexpansion(ArgTok, PP))
431       ResultArgToks = &(const_cast<clang::MacroArgs *>(Args))
432           ->getPreExpArgument(ArgNo, PP)[0];
433     else
434       ResultArgToks = ArgTok; // Use non-preexpanded Tokens.
435     // If the arg token didn't expand into anything, ignore it.
436     if (ResultArgToks->is(clang::tok::eof))
437       continue;
438     unsigned NumToks = clang::MacroArgs::getArgLength(ResultArgToks);
439     // Append the resulting argument expansions.
440     for (unsigned ArgumentIndex = 0; ArgumentIndex < NumToks; ++ArgumentIndex) {
441       const clang::Token &AT = ResultArgToks[ArgumentIndex];
442       clang::IdentifierInfo *II = AT.getIdentifierInfo();
443       if (II == nullptr)
444         Expanded += PP.getSpelling(AT); // Not an identifier.
445       else {
446         // It's an identifier.  Check for further expansion.
447         std::string Name = II->getName().str();
448         clang::MacroInfo *MacroInfo = PP.getMacroInfo(II);
449         if (MacroInfo)
450           Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr);
451         else
452           Expanded += Name;
453       }
454     }
455   }
456   return Expanded;
457 }
458 
459 namespace {
460 
461 // ConditionValueKind strings.
462 const char *
463 ConditionValueKindStrings[] = {
464   "(not evaluated)", "false", "true"
465 };
466 
467 // Preprocessor item key.
468 //
469 // This class represents a location in a source file, for use
470 // as a key representing a unique name/file/line/column quadruplet,
471 // which in this case is used to identify a macro expansion instance,
472 // but could be used for other things as well.
473 // The file is a header file handle, the line is a line number,
474 // and the column is a column number.
475 class PPItemKey {
476 public:
PPItemKey(clang::Preprocessor & PP,StringHandle Name,HeaderHandle File,clang::SourceLocation Loc)477   PPItemKey(clang::Preprocessor &PP, StringHandle Name, HeaderHandle File,
478             clang::SourceLocation Loc)
479       : Name(Name), File(File) {
480     getSourceLocationLineAndColumn(PP, Loc, Line, Column);
481   }
PPItemKey(StringHandle Name,HeaderHandle File,int Line,int Column)482   PPItemKey(StringHandle Name, HeaderHandle File, int Line, int Column)
483       : Name(Name), File(File), Line(Line), Column(Column) {}
PPItemKey(const PPItemKey & Other)484   PPItemKey(const PPItemKey &Other)
485       : Name(Other.Name), File(Other.File), Line(Other.Line),
486         Column(Other.Column) {}
PPItemKey()487   PPItemKey() : File(HeaderHandleInvalid), Line(0), Column(0) {}
operator ==(const PPItemKey & Other) const488   bool operator==(const PPItemKey &Other) const {
489     if (Name != Other.Name)
490       return false;
491     if (File != Other.File)
492       return false;
493     if (Line != Other.Line)
494       return false;
495     return Column == Other.Column;
496   }
operator <(const PPItemKey & Other) const497   bool operator<(const PPItemKey &Other) const {
498     if (Name < Other.Name)
499       return true;
500     else if (Name > Other.Name)
501       return false;
502     if (File < Other.File)
503       return true;
504     else if (File > Other.File)
505       return false;
506     if (Line < Other.Line)
507       return true;
508     else if (Line > Other.Line)
509       return false;
510     return Column < Other.Column;
511   }
512   StringHandle Name;
513   HeaderHandle File;
514   int Line;
515   int Column;
516 };
517 
518 // Header inclusion path.
519 class HeaderInclusionPath {
520 public:
HeaderInclusionPath(std::vector<HeaderHandle> HeaderInclusionPath)521   HeaderInclusionPath(std::vector<HeaderHandle> HeaderInclusionPath)
522       : Path(HeaderInclusionPath) {}
HeaderInclusionPath(const HeaderInclusionPath & Other)523   HeaderInclusionPath(const HeaderInclusionPath &Other) : Path(Other.Path) {}
HeaderInclusionPath()524   HeaderInclusionPath() {}
525   std::vector<HeaderHandle> Path;
526 };
527 
528 // Macro expansion instance.
529 //
530 // This class represents an instance of a macro expansion with a
531 // unique value.  It also stores the unique header inclusion paths
532 // for use in telling the user the nested include path to the header.
533 class MacroExpansionInstance {
534 public:
MacroExpansionInstance(StringHandle MacroExpanded,PPItemKey & DefinitionLocation,StringHandle DefinitionSourceLine,InclusionPathHandle H)535   MacroExpansionInstance(StringHandle MacroExpanded,
536                          PPItemKey &DefinitionLocation,
537                          StringHandle DefinitionSourceLine,
538                          InclusionPathHandle H)
539       : MacroExpanded(MacroExpanded), DefinitionLocation(DefinitionLocation),
540         DefinitionSourceLine(DefinitionSourceLine) {
541     InclusionPathHandles.push_back(H);
542   }
MacroExpansionInstance()543   MacroExpansionInstance() {}
544 
545   // Check for the presence of a header inclusion path handle entry.
546   // Return false if not found.
haveInclusionPathHandle(InclusionPathHandle H)547   bool haveInclusionPathHandle(InclusionPathHandle H) {
548     for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
549          I != E; ++I) {
550       if (*I == H)
551         return true;
552     }
553     return InclusionPathHandleInvalid;
554   }
555   // Add a new header inclusion path entry, if not already present.
addInclusionPathHandle(InclusionPathHandle H)556   void addInclusionPathHandle(InclusionPathHandle H) {
557     if (!haveInclusionPathHandle(H))
558       InclusionPathHandles.push_back(H);
559   }
560 
561   // A string representing the macro instance after preprocessing.
562   StringHandle MacroExpanded;
563   // A file/line/column triplet representing the macro definition location.
564   PPItemKey DefinitionLocation;
565   // A place to save the macro definition line string.
566   StringHandle DefinitionSourceLine;
567   // The header inclusion path handles for all the instances.
568   std::vector<InclusionPathHandle> InclusionPathHandles;
569 };
570 
571 // Macro expansion instance tracker.
572 //
573 // This class represents one macro expansion, keyed by a PPItemKey.
574 // It stores a string representing the macro reference in the source,
575 // and a list of ConditionalExpansionInstances objects representing
576 // the unique values the condition expands to in instances of the header.
577 class MacroExpansionTracker {
578 public:
MacroExpansionTracker(StringHandle MacroUnexpanded,StringHandle MacroExpanded,StringHandle InstanceSourceLine,PPItemKey & DefinitionLocation,StringHandle DefinitionSourceLine,InclusionPathHandle InclusionPathHandle)579   MacroExpansionTracker(StringHandle MacroUnexpanded,
580                         StringHandle MacroExpanded,
581                         StringHandle InstanceSourceLine,
582                         PPItemKey &DefinitionLocation,
583                         StringHandle DefinitionSourceLine,
584                         InclusionPathHandle InclusionPathHandle)
585       : MacroUnexpanded(MacroUnexpanded),
586         InstanceSourceLine(InstanceSourceLine) {
587     addMacroExpansionInstance(MacroExpanded, DefinitionLocation,
588                               DefinitionSourceLine, InclusionPathHandle);
589   }
MacroExpansionTracker()590   MacroExpansionTracker() {}
591 
592   // Find a matching macro expansion instance.
593   MacroExpansionInstance *
findMacroExpansionInstance(StringHandle MacroExpanded,PPItemKey & DefinitionLocation)594   findMacroExpansionInstance(StringHandle MacroExpanded,
595                              PPItemKey &DefinitionLocation) {
596     for (auto I = MacroExpansionInstances.begin(),
597               E = MacroExpansionInstances.end();
598          I != E; ++I) {
599       if ((I->MacroExpanded == MacroExpanded) &&
600           (I->DefinitionLocation == DefinitionLocation)) {
601         return &*I; // Found.
602       }
603     }
604     return nullptr; // Not found.
605   }
606 
607   // Add a macro expansion instance.
addMacroExpansionInstance(StringHandle MacroExpanded,PPItemKey & DefinitionLocation,StringHandle DefinitionSourceLine,InclusionPathHandle InclusionPathHandle)608   void addMacroExpansionInstance(StringHandle MacroExpanded,
609                                  PPItemKey &DefinitionLocation,
610                                  StringHandle DefinitionSourceLine,
611                                  InclusionPathHandle InclusionPathHandle) {
612     MacroExpansionInstances.push_back(
613         MacroExpansionInstance(MacroExpanded, DefinitionLocation,
614                                DefinitionSourceLine, InclusionPathHandle));
615   }
616 
617   // Return true if there is a mismatch.
hasMismatch()618   bool hasMismatch() { return MacroExpansionInstances.size() > 1; }
619 
620   // A string representing the macro instance without expansion.
621   StringHandle MacroUnexpanded;
622   // A place to save the macro instance source line string.
623   StringHandle InstanceSourceLine;
624   // The macro expansion instances.
625   // If all instances of the macro expansion expand to the same value,
626   // This vector will only have one instance.
627   std::vector<MacroExpansionInstance> MacroExpansionInstances;
628 };
629 
630 // Conditional expansion instance.
631 //
632 // This class represents an instance of a condition exoression result
633 // with a unique value.  It also stores the unique header inclusion paths
634 // for use in telling the user the nested include path to the header.
635 class ConditionalExpansionInstance {
636 public:
ConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue,InclusionPathHandle H)637   ConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue, InclusionPathHandle H)
638       : ConditionValue(ConditionValue) {
639     InclusionPathHandles.push_back(H);
640   }
ConditionalExpansionInstance()641   ConditionalExpansionInstance() {}
642 
643   // Check for the presence of a header inclusion path handle entry.
644   // Return false if not found.
haveInclusionPathHandle(InclusionPathHandle H)645   bool haveInclusionPathHandle(InclusionPathHandle H) {
646     for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
647          I != E; ++I) {
648       if (*I == H)
649         return true;
650     }
651     return InclusionPathHandleInvalid;
652   }
653   // Add a new header inclusion path entry, if not already present.
addInclusionPathHandle(InclusionPathHandle H)654   void addInclusionPathHandle(InclusionPathHandle H) {
655     if (!haveInclusionPathHandle(H))
656       InclusionPathHandles.push_back(H);
657   }
658 
659   // A flag representing the evaluated condition value.
660   clang::PPCallbacks::ConditionValueKind ConditionValue;
661   // The header inclusion path handles for all the instances.
662   std::vector<InclusionPathHandle> InclusionPathHandles;
663 };
664 
665 // Conditional directive instance tracker.
666 //
667 // This class represents one conditional directive, keyed by a PPItemKey.
668 // It stores a string representing the macro reference in the source,
669 // and a list of ConditionExpansionInstance objects representing
670 // the unique value the condition expression expands to in instances of
671 // the header.
672 class ConditionalTracker {
673 public:
ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind,clang::PPCallbacks::ConditionValueKind ConditionValue,StringHandle ConditionUnexpanded,InclusionPathHandle InclusionPathHandle)674   ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind,
675                      clang::PPCallbacks::ConditionValueKind ConditionValue,
676                      StringHandle ConditionUnexpanded,
677                      InclusionPathHandle InclusionPathHandle)
678       : DirectiveKind(DirectiveKind), ConditionUnexpanded(ConditionUnexpanded) {
679     addConditionalExpansionInstance(ConditionValue, InclusionPathHandle);
680   }
ConditionalTracker()681   ConditionalTracker() {}
682 
683   // Find a matching condition expansion instance.
684   ConditionalExpansionInstance *
findConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue)685   findConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue) {
686     for (auto I = ConditionalExpansionInstances.begin(),
687               E = ConditionalExpansionInstances.end();
688          I != E; ++I) {
689       if (I->ConditionValue == ConditionValue) {
690         return &*I; // Found.
691       }
692     }
693     return nullptr; // Not found.
694   }
695 
696   // Add a conditional expansion instance.
697   void
addConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue,InclusionPathHandle InclusionPathHandle)698   addConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue,
699                                   InclusionPathHandle InclusionPathHandle) {
700     ConditionalExpansionInstances.push_back(
701         ConditionalExpansionInstance(ConditionValue, InclusionPathHandle));
702   }
703 
704   // Return true if there is a mismatch.
hasMismatch()705   bool hasMismatch() { return ConditionalExpansionInstances.size() > 1; }
706 
707   // The kind of directive.
708   clang::tok::PPKeywordKind DirectiveKind;
709   // A string representing the macro instance without expansion.
710   StringHandle ConditionUnexpanded;
711   // The condition expansion instances.
712   // If all instances of the conditional expression expand to the same value,
713   // This vector will only have one instance.
714   std::vector<ConditionalExpansionInstance> ConditionalExpansionInstances;
715 };
716 
717 class PreprocessorTrackerImpl;
718 
719 // Preprocessor callbacks for modularize.
720 //
721 // This class derives from the Clang PPCallbacks class to track preprocessor
722 // actions, such as changing files and handling preprocessor directives and
723 // macro expansions.  It has to figure out when a new header file is entered
724 // and left, as the provided handler is not particularly clear about it.
725 class PreprocessorCallbacks : public clang::PPCallbacks {
726 public:
PreprocessorCallbacks(PreprocessorTrackerImpl & ppTracker,clang::Preprocessor & PP,llvm::StringRef rootHeaderFile)727   PreprocessorCallbacks(PreprocessorTrackerImpl &ppTracker,
728                         clang::Preprocessor &PP, llvm::StringRef rootHeaderFile)
729       : PPTracker(ppTracker), PP(PP), RootHeaderFile(rootHeaderFile) {}
~PreprocessorCallbacks()730   ~PreprocessorCallbacks() override {}
731 
732   // Overridden handlers.
733   void
734   InclusionDirective(clang::SourceLocation HashLoc,
735                      const clang::Token &IncludeTok, llvm::StringRef FileName,
736                      bool IsAngled, clang::CharSourceRange FilenameRange,
737                      clang::OptionalFileEntryRef File,
738                      llvm::StringRef SearchPath, llvm::StringRef RelativePath,
739                      const clang::Module *SuggestedModule, bool ModuleImported,
740                      clang::SrcMgr::CharacteristicKind FileType) override;
741   void FileChanged(clang::SourceLocation Loc,
742                    clang::PPCallbacks::FileChangeReason Reason,
743                    clang::SrcMgr::CharacteristicKind FileType,
744                    clang::FileID PrevFID = clang::FileID()) override;
745   void MacroExpands(const clang::Token &MacroNameTok,
746                     const clang::MacroDefinition &MD, clang::SourceRange Range,
747                     const clang::MacroArgs *Args) override;
748   void Defined(const clang::Token &MacroNameTok,
749                const clang::MacroDefinition &MD,
750                clang::SourceRange Range) override;
751   void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
752           clang::PPCallbacks::ConditionValueKind ConditionResult) override;
753   void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
754             clang::PPCallbacks::ConditionValueKind ConditionResult,
755             clang::SourceLocation IfLoc) override;
756   void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
757              const clang::MacroDefinition &MD) override;
758   void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
759               const clang::MacroDefinition &MD) override;
760 
761 private:
762   PreprocessorTrackerImpl &PPTracker;
763   clang::Preprocessor &PP;
764   std::string RootHeaderFile;
765 };
766 
767 // Preprocessor macro expansion item map types.
768 typedef std::map<PPItemKey, MacroExpansionTracker> MacroExpansionMap;
769 typedef std::map<PPItemKey, MacroExpansionTracker>::iterator
770 MacroExpansionMapIter;
771 
772 // Preprocessor conditional expansion item map types.
773 typedef std::map<PPItemKey, ConditionalTracker> ConditionalExpansionMap;
774 typedef std::map<PPItemKey, ConditionalTracker>::iterator
775 ConditionalExpansionMapIter;
776 
777 // Preprocessor tracker for modularize.
778 //
779 // This class stores information about all the headers processed in the
780 // course of running modularize.
781 class PreprocessorTrackerImpl : public PreprocessorTracker {
782 public:
PreprocessorTrackerImpl(llvm::SmallVector<std::string,32> & Headers,bool DoBlockCheckHeaderListOnly)783   PreprocessorTrackerImpl(llvm::SmallVector<std::string, 32> &Headers,
784         bool DoBlockCheckHeaderListOnly)
785       : BlockCheckHeaderListOnly(DoBlockCheckHeaderListOnly),
786         CurrentInclusionPathHandle(InclusionPathHandleInvalid),
787         InNestedHeader(false) {
788     // Use canonical header path representation.
789     for (llvm::ArrayRef<std::string>::iterator I = Headers.begin(),
790       E = Headers.end();
791       I != E; ++I) {
792       HeaderList.push_back(getCanonicalPath(*I));
793     }
794   }
795 
~PreprocessorTrackerImpl()796   ~PreprocessorTrackerImpl() override {}
797 
798   // Handle entering a preprocessing session.
handlePreprocessorEntry(clang::Preprocessor & PP,llvm::StringRef rootHeaderFile)799   void handlePreprocessorEntry(clang::Preprocessor &PP,
800                                llvm::StringRef rootHeaderFile) override {
801     HeadersInThisCompile.clear();
802     assert((HeaderStack.size() == 0) && "Header stack should be empty.");
803     pushHeaderHandle(addHeader(rootHeaderFile));
804     PP.addPPCallbacks(std::make_unique<PreprocessorCallbacks>(*this, PP,
805                                                                rootHeaderFile));
806   }
807   // Handle exiting a preprocessing session.
handlePreprocessorExit()808   void handlePreprocessorExit() override { HeaderStack.clear(); }
809 
810   // Handle include directive.
811   // This function is called every time an include directive is seen by the
812   // preprocessor, for the purpose of later checking for 'extern "" {}' or
813   // "namespace {}" blocks containing #include directives.
handleIncludeDirective(llvm::StringRef DirectivePath,int DirectiveLine,int DirectiveColumn,llvm::StringRef TargetPath)814   void handleIncludeDirective(llvm::StringRef DirectivePath, int DirectiveLine,
815                               int DirectiveColumn,
816                               llvm::StringRef TargetPath) override {
817     // If it's not a header in the header list, ignore it with respect to
818     // the check.
819     if (BlockCheckHeaderListOnly && !isHeaderListHeader(TargetPath))
820       return;
821     HeaderHandle CurrentHeaderHandle = findHeaderHandle(DirectivePath);
822     StringHandle IncludeHeaderHandle = addString(TargetPath);
823     for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),
824                                                 E = IncludeDirectives.end();
825          I != E; ++I) {
826       // If we already have an entry for this directive, return now.
827       if ((I->File == CurrentHeaderHandle) && (I->Line == DirectiveLine))
828         return;
829     }
830     PPItemKey IncludeDirectiveItem(IncludeHeaderHandle, CurrentHeaderHandle,
831                                    DirectiveLine, DirectiveColumn);
832     IncludeDirectives.push_back(IncludeDirectiveItem);
833   }
834 
835   // Check for include directives within the given source line range.
836   // Report errors if any found.  Returns true if no include directives
837   // found in block.
checkForIncludesInBlock(clang::Preprocessor & PP,clang::SourceRange BlockSourceRange,const char * BlockIdentifierMessage,llvm::raw_ostream & OS)838   bool checkForIncludesInBlock(clang::Preprocessor &PP,
839                                clang::SourceRange BlockSourceRange,
840                                const char *BlockIdentifierMessage,
841                                llvm::raw_ostream &OS) override {
842     clang::SourceLocation BlockStartLoc = BlockSourceRange.getBegin();
843     clang::SourceLocation BlockEndLoc = BlockSourceRange.getEnd();
844     // Use block location to get FileID of both the include directive
845     // and block statement.
846     clang::FileID FileID = PP.getSourceManager().getFileID(BlockStartLoc);
847     std::string SourcePath = getSourceLocationFile(PP, BlockStartLoc);
848     SourcePath = ModularizeUtilities::getCanonicalPath(SourcePath);
849     HeaderHandle SourceHandle = findHeaderHandle(SourcePath);
850     if (SourceHandle == -1)
851       return true;
852     int BlockStartLine, BlockStartColumn, BlockEndLine, BlockEndColumn;
853     bool returnValue = true;
854     getSourceLocationLineAndColumn(PP, BlockStartLoc, BlockStartLine,
855                                    BlockStartColumn);
856     getSourceLocationLineAndColumn(PP, BlockEndLoc, BlockEndLine,
857                                    BlockEndColumn);
858     for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),
859                                                 E = IncludeDirectives.end();
860          I != E; ++I) {
861       // If we find an entry within the block, report an error.
862       if ((I->File == SourceHandle) && (I->Line >= BlockStartLine) &&
863           (I->Line < BlockEndLine)) {
864         returnValue = false;
865         OS << SourcePath << ":" << I->Line << ":" << I->Column << ":\n";
866         OS << getSourceLine(PP, FileID, I->Line) << "\n";
867         if (I->Column > 0)
868           OS << std::string(I->Column - 1, ' ') << "^\n";
869         OS << "error: Include directive within " << BlockIdentifierMessage
870            << ".\n";
871         OS << SourcePath << ":" << BlockStartLine << ":" << BlockStartColumn
872            << ":\n";
873         OS << getSourceLine(PP, BlockStartLoc) << "\n";
874         if (BlockStartColumn > 0)
875           OS << std::string(BlockStartColumn - 1, ' ') << "^\n";
876         OS << "The \"" << BlockIdentifierMessage << "\" block is here.\n";
877       }
878     }
879     return returnValue;
880   }
881 
882   // Handle entering a header source file.
handleHeaderEntry(clang::Preprocessor & PP,llvm::StringRef HeaderPath)883   void handleHeaderEntry(clang::Preprocessor &PP, llvm::StringRef HeaderPath) {
884     // Ignore <built-in> and <command-line> to reduce message clutter.
885     if (HeaderPath.starts_with("<"))
886       return;
887     HeaderHandle H = addHeader(HeaderPath);
888     if (H != getCurrentHeaderHandle())
889       pushHeaderHandle(H);
890     // Check for nested header.
891     if (!InNestedHeader)
892       InNestedHeader = !HeadersInThisCompile.insert(H).second;
893   }
894 
895   // Handle exiting a header source file.
handleHeaderExit(llvm::StringRef HeaderPath)896   void handleHeaderExit(llvm::StringRef HeaderPath) {
897     // Ignore <built-in> and <command-line> to reduce message clutter.
898     if (HeaderPath.starts_with("<"))
899       return;
900     HeaderHandle H = findHeaderHandle(HeaderPath);
901     HeaderHandle TH;
902     if (isHeaderHandleInStack(H)) {
903       do {
904         TH = getCurrentHeaderHandle();
905         popHeaderHandle();
906       } while ((TH != H) && (HeaderStack.size() != 0));
907     }
908     InNestedHeader = false;
909   }
910 
911   // Lookup/add string.
addString(llvm::StringRef Str)912   StringHandle addString(llvm::StringRef Str) {
913     return Strings.insert(Str).first->first();
914   }
915 
916   // Convert to a canonical path.
getCanonicalPath(llvm::StringRef path) const917   std::string getCanonicalPath(llvm::StringRef path) const {
918     std::string CanonicalPath(path);
919     std::replace(CanonicalPath.begin(), CanonicalPath.end(), '\\', '/');
920     return CanonicalPath;
921   }
922 
923   // Return true if the given header is in the header list.
isHeaderListHeader(llvm::StringRef HeaderPath) const924   bool isHeaderListHeader(llvm::StringRef HeaderPath) const {
925     std::string CanonicalPath = getCanonicalPath(HeaderPath);
926     for (llvm::ArrayRef<std::string>::iterator I = HeaderList.begin(),
927         E = HeaderList.end();
928         I != E; ++I) {
929       if (*I == CanonicalPath)
930         return true;
931     }
932     return false;
933   }
934 
935   // Get the handle of a header file entry.
936   // Return HeaderHandleInvalid if not found.
findHeaderHandle(llvm::StringRef HeaderPath) const937   HeaderHandle findHeaderHandle(llvm::StringRef HeaderPath) const {
938     std::string CanonicalPath = getCanonicalPath(HeaderPath);
939     HeaderHandle H = 0;
940     for (auto I = HeaderPaths.begin(), E = HeaderPaths.end(); I != E;
941          ++I, ++H) {
942       if (*I == CanonicalPath)
943         return H;
944     }
945     return HeaderHandleInvalid;
946   }
947 
948   // Add a new header file entry, or return existing handle.
949   // Return the header handle.
addHeader(llvm::StringRef HeaderPath)950   HeaderHandle addHeader(llvm::StringRef HeaderPath) {
951     std::string CanonicalPath = getCanonicalPath(HeaderPath);
952     HeaderHandle H = findHeaderHandle(CanonicalPath);
953     if (H == HeaderHandleInvalid) {
954       H = HeaderPaths.size();
955       HeaderPaths.push_back(addString(CanonicalPath));
956     }
957     return H;
958   }
959 
960   // Return a header file path string given its handle.
getHeaderFilePath(HeaderHandle H) const961   StringHandle getHeaderFilePath(HeaderHandle H) const {
962     if ((H >= 0) && (H < (HeaderHandle)HeaderPaths.size()))
963       return HeaderPaths[H];
964     return StringHandle();
965   }
966 
967   // Returns a handle to the inclusion path.
pushHeaderHandle(HeaderHandle H)968   InclusionPathHandle pushHeaderHandle(HeaderHandle H) {
969     HeaderStack.push_back(H);
970     return CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack);
971   }
972   // Pops the last header handle from the stack;
popHeaderHandle()973   void popHeaderHandle() {
974     // assert((HeaderStack.size() != 0) && "Header stack already empty.");
975     if (HeaderStack.size() != 0) {
976       HeaderStack.pop_back();
977       CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack);
978     }
979   }
980   // Get the top handle on the header stack.
getCurrentHeaderHandle() const981   HeaderHandle getCurrentHeaderHandle() const {
982     if (HeaderStack.size() != 0)
983       return HeaderStack.back();
984     return HeaderHandleInvalid;
985   }
986 
987   // Check for presence of header handle in the header stack.
isHeaderHandleInStack(HeaderHandle H) const988   bool isHeaderHandleInStack(HeaderHandle H) const {
989     return llvm::is_contained(HeaderStack, H);
990   }
991 
992   // Get the handle of a header inclusion path entry.
993   // Return InclusionPathHandleInvalid if not found.
994   InclusionPathHandle
findInclusionPathHandle(const std::vector<HeaderHandle> & Path) const995   findInclusionPathHandle(const std::vector<HeaderHandle> &Path) const {
996     InclusionPathHandle H = 0;
997     for (auto I = InclusionPaths.begin(), E = InclusionPaths.end(); I != E;
998          ++I, ++H) {
999       if (I->Path == Path)
1000         return H;
1001     }
1002     return HeaderHandleInvalid;
1003   }
1004   // Add a new header inclusion path entry, or return existing handle.
1005   // Return the header inclusion path entry handle.
1006   InclusionPathHandle
addInclusionPathHandle(const std::vector<HeaderHandle> & Path)1007   addInclusionPathHandle(const std::vector<HeaderHandle> &Path) {
1008     InclusionPathHandle H = findInclusionPathHandle(Path);
1009     if (H == HeaderHandleInvalid) {
1010       H = InclusionPaths.size();
1011       InclusionPaths.push_back(HeaderInclusionPath(Path));
1012     }
1013     return H;
1014   }
1015   // Return the current inclusion path handle.
getCurrentInclusionPathHandle() const1016   InclusionPathHandle getCurrentInclusionPathHandle() const {
1017     return CurrentInclusionPathHandle;
1018   }
1019 
1020   // Return an inclusion path given its handle.
1021   const std::vector<HeaderHandle> &
getInclusionPath(InclusionPathHandle H) const1022   getInclusionPath(InclusionPathHandle H) const {
1023     if ((H >= 0) && (H <= (InclusionPathHandle)InclusionPaths.size()))
1024       return InclusionPaths[H].Path;
1025     static std::vector<HeaderHandle> Empty;
1026     return Empty;
1027   }
1028 
1029   // Add a macro expansion instance.
addMacroExpansionInstance(clang::Preprocessor & PP,HeaderHandle H,clang::SourceLocation InstanceLoc,clang::SourceLocation DefinitionLoc,clang::IdentifierInfo * II,llvm::StringRef MacroUnexpanded,llvm::StringRef MacroExpanded,InclusionPathHandle InclusionPathHandle)1030   void addMacroExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
1031                                  clang::SourceLocation InstanceLoc,
1032                                  clang::SourceLocation DefinitionLoc,
1033                                  clang::IdentifierInfo *II,
1034                                  llvm::StringRef MacroUnexpanded,
1035                                  llvm::StringRef MacroExpanded,
1036                                  InclusionPathHandle InclusionPathHandle) {
1037     if (InNestedHeader)
1038       return;
1039     StringHandle MacroName = addString(II->getName());
1040     PPItemKey InstanceKey(PP, MacroName, H, InstanceLoc);
1041     PPItemKey DefinitionKey(PP, MacroName, H, DefinitionLoc);
1042     auto I = MacroExpansions.find(InstanceKey);
1043     // If existing instance of expansion not found, add one.
1044     if (I == MacroExpansions.end()) {
1045       std::string InstanceSourceLine =
1046           getSourceLocationString(PP, InstanceLoc) + ":\n" +
1047           getSourceLine(PP, InstanceLoc) + "\n";
1048       std::string DefinitionSourceLine =
1049           getSourceLocationString(PP, DefinitionLoc) + ":\n" +
1050           getSourceLine(PP, DefinitionLoc) + "\n";
1051       MacroExpansions[InstanceKey] = MacroExpansionTracker(
1052           addString(MacroUnexpanded), addString(MacroExpanded),
1053           addString(InstanceSourceLine), DefinitionKey,
1054           addString(DefinitionSourceLine), InclusionPathHandle);
1055     } else {
1056       // We've seen the macro before.  Get its tracker.
1057       MacroExpansionTracker &CondTracker = I->second;
1058       // Look up an existing instance value for the macro.
1059       MacroExpansionInstance *MacroInfo =
1060           CondTracker.findMacroExpansionInstance(addString(MacroExpanded),
1061                                                  DefinitionKey);
1062       // If found, just add the inclusion path to the instance.
1063       if (MacroInfo)
1064         MacroInfo->addInclusionPathHandle(InclusionPathHandle);
1065       else {
1066         // Otherwise add a new instance with the unique value.
1067         std::string DefinitionSourceLine =
1068             getSourceLocationString(PP, DefinitionLoc) + ":\n" +
1069             getSourceLine(PP, DefinitionLoc) + "\n";
1070         CondTracker.addMacroExpansionInstance(
1071             addString(MacroExpanded), DefinitionKey,
1072             addString(DefinitionSourceLine), InclusionPathHandle);
1073       }
1074     }
1075   }
1076 
1077   // Add a conditional expansion instance.
1078   void
addConditionalExpansionInstance(clang::Preprocessor & PP,HeaderHandle H,clang::SourceLocation InstanceLoc,clang::tok::PPKeywordKind DirectiveKind,clang::PPCallbacks::ConditionValueKind ConditionValue,llvm::StringRef ConditionUnexpanded,InclusionPathHandle InclusionPathHandle)1079   addConditionalExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
1080                                   clang::SourceLocation InstanceLoc,
1081                                   clang::tok::PPKeywordKind DirectiveKind,
1082                                   clang::PPCallbacks::ConditionValueKind ConditionValue,
1083                                   llvm::StringRef ConditionUnexpanded,
1084                                   InclusionPathHandle InclusionPathHandle) {
1085     // Ignore header guards, assuming the header guard is the only conditional.
1086     if (InNestedHeader)
1087       return;
1088     StringHandle ConditionUnexpandedHandle(addString(ConditionUnexpanded));
1089     PPItemKey InstanceKey(PP, ConditionUnexpandedHandle, H, InstanceLoc);
1090     auto I = ConditionalExpansions.find(InstanceKey);
1091     // If existing instance of condition not found, add one.
1092     if (I == ConditionalExpansions.end()) {
1093       std::string InstanceSourceLine =
1094           getSourceLocationString(PP, InstanceLoc) + ":\n" +
1095           getSourceLine(PP, InstanceLoc) + "\n";
1096       ConditionalExpansions[InstanceKey] =
1097           ConditionalTracker(DirectiveKind, ConditionValue,
1098                              ConditionUnexpandedHandle, InclusionPathHandle);
1099     } else {
1100       // We've seen the conditional before.  Get its tracker.
1101       ConditionalTracker &CondTracker = I->second;
1102       // Look up an existing instance value for the condition.
1103       ConditionalExpansionInstance *MacroInfo =
1104           CondTracker.findConditionalExpansionInstance(ConditionValue);
1105       // If found, just add the inclusion path to the instance.
1106       if (MacroInfo)
1107         MacroInfo->addInclusionPathHandle(InclusionPathHandle);
1108       else {
1109         // Otherwise add a new instance with the unique value.
1110         CondTracker.addConditionalExpansionInstance(ConditionValue,
1111                                                     InclusionPathHandle);
1112       }
1113     }
1114   }
1115 
1116   // Report on inconsistent macro instances.
1117   // Returns true if any mismatches.
reportInconsistentMacros(llvm::raw_ostream & OS)1118   bool reportInconsistentMacros(llvm::raw_ostream &OS) override {
1119     bool ReturnValue = false;
1120     // Walk all the macro expansion trackers in the map.
1121     for (auto I = MacroExpansions.begin(), E = MacroExpansions.end(); I != E;
1122          ++I) {
1123       const PPItemKey &ItemKey = I->first;
1124       MacroExpansionTracker &MacroExpTracker = I->second;
1125       // If no mismatch (only one instance value) continue.
1126       if (!MacroExpTracker.hasMismatch())
1127         continue;
1128       // Tell caller we found one or more errors.
1129       ReturnValue = true;
1130       // Start the error message.
1131       OS << MacroExpTracker.InstanceSourceLine;
1132       if (ItemKey.Column > 0)
1133         OS << std::string(ItemKey.Column - 1, ' ') << "^\n";
1134       OS << "error: Macro instance '" << MacroExpTracker.MacroUnexpanded
1135          << "' has different values in this header, depending on how it was "
1136             "included.\n";
1137       // Walk all the instances.
1138       for (auto IMT = MacroExpTracker.MacroExpansionInstances.begin(),
1139                 EMT = MacroExpTracker.MacroExpansionInstances.end();
1140            IMT != EMT; ++IMT) {
1141         MacroExpansionInstance &MacroInfo = *IMT;
1142         OS << "  '" << MacroExpTracker.MacroUnexpanded << "' expanded to: '"
1143            << MacroInfo.MacroExpanded
1144            << "' with respect to these inclusion paths:\n";
1145         // Walk all the inclusion path hierarchies.
1146         for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
1147                   EIP = MacroInfo.InclusionPathHandles.end();
1148              IIP != EIP; ++IIP) {
1149           const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
1150           auto Count = (int)ip.size();
1151           for (int Index = 0; Index < Count; ++Index) {
1152             HeaderHandle H = ip[Index];
1153             OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H)
1154                << "\n";
1155           }
1156         }
1157         // For a macro that wasn't defined, we flag it by using the
1158         // instance location.
1159         // If there is a definition...
1160         if (MacroInfo.DefinitionLocation.Line != ItemKey.Line) {
1161           OS << MacroInfo.DefinitionSourceLine;
1162           if (MacroInfo.DefinitionLocation.Column > 0)
1163             OS << std::string(MacroInfo.DefinitionLocation.Column - 1, ' ')
1164                << "^\n";
1165           OS << "Macro defined here.\n";
1166         } else
1167           OS << "(no macro definition)"
1168              << "\n";
1169       }
1170     }
1171     return ReturnValue;
1172   }
1173 
1174   // Report on inconsistent conditional instances.
1175   // Returns true if any mismatches.
reportInconsistentConditionals(llvm::raw_ostream & OS)1176   bool reportInconsistentConditionals(llvm::raw_ostream &OS) override {
1177     bool ReturnValue = false;
1178     // Walk all the conditional trackers in the map.
1179     for (auto I = ConditionalExpansions.begin(),
1180               E = ConditionalExpansions.end();
1181          I != E; ++I) {
1182       const PPItemKey &ItemKey = I->first;
1183       ConditionalTracker &CondTracker = I->second;
1184       if (!CondTracker.hasMismatch())
1185         continue;
1186       // Tell caller we found one or more errors.
1187       ReturnValue = true;
1188       // Start the error message.
1189       OS << HeaderPaths[ItemKey.File] << ":" << ItemKey.Line << ":"
1190          << ItemKey.Column << "\n";
1191       OS << "#" << getDirectiveSpelling(CondTracker.DirectiveKind) << " "
1192          << CondTracker.ConditionUnexpanded << "\n";
1193       OS << "^\n";
1194       OS << "error: Conditional expression instance '"
1195          << CondTracker.ConditionUnexpanded
1196          << "' has different values in this header, depending on how it was "
1197             "included.\n";
1198       // Walk all the instances.
1199       for (auto IMT = CondTracker.ConditionalExpansionInstances.begin(),
1200                 EMT = CondTracker.ConditionalExpansionInstances.end();
1201            IMT != EMT; ++IMT) {
1202         ConditionalExpansionInstance &MacroInfo = *IMT;
1203         OS << "  '" << CondTracker.ConditionUnexpanded << "' expanded to: '"
1204            << ConditionValueKindStrings[MacroInfo.ConditionValue]
1205            << "' with respect to these inclusion paths:\n";
1206         // Walk all the inclusion path hierarchies.
1207         for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
1208                   EIP = MacroInfo.InclusionPathHandles.end();
1209              IIP != EIP; ++IIP) {
1210           const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
1211           auto Count = (int)ip.size();
1212           for (int Index = 0; Index < Count; ++Index) {
1213             HeaderHandle H = ip[Index];
1214             OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H)
1215                << "\n";
1216           }
1217         }
1218       }
1219     }
1220     return ReturnValue;
1221   }
1222 
1223   // Get directive spelling.
getDirectiveSpelling(clang::tok::PPKeywordKind kind)1224   static const char *getDirectiveSpelling(clang::tok::PPKeywordKind kind) {
1225     switch (kind) {
1226     case clang::tok::pp_if:
1227       return "if";
1228     case clang::tok::pp_elif:
1229       return "elif";
1230     case clang::tok::pp_ifdef:
1231       return "ifdef";
1232     case clang::tok::pp_ifndef:
1233       return "ifndef";
1234     default:
1235       return "(unknown)";
1236     }
1237   }
1238 
1239 private:
1240   llvm::SmallVector<std::string, 32> HeaderList;
1241   // Only do extern, namespace check for headers in HeaderList.
1242   bool BlockCheckHeaderListOnly;
1243   llvm::StringSet<> Strings;
1244   std::vector<StringHandle> HeaderPaths;
1245   std::vector<HeaderHandle> HeaderStack;
1246   std::vector<HeaderInclusionPath> InclusionPaths;
1247   InclusionPathHandle CurrentInclusionPathHandle;
1248   llvm::SmallSet<HeaderHandle, 32> HeadersInThisCompile;
1249   std::vector<PPItemKey> IncludeDirectives;
1250   MacroExpansionMap MacroExpansions;
1251   ConditionalExpansionMap ConditionalExpansions;
1252   bool InNestedHeader;
1253 };
1254 
1255 } // namespace
1256 
1257 // PreprocessorTracker functions.
1258 
1259 // PreprocessorTracker destructor.
~PreprocessorTracker()1260 PreprocessorTracker::~PreprocessorTracker() {}
1261 
1262 // Create instance of PreprocessorTracker.
create(llvm::SmallVector<std::string,32> & Headers,bool DoBlockCheckHeaderListOnly)1263 PreprocessorTracker *PreprocessorTracker::create(
1264     llvm::SmallVector<std::string, 32> &Headers,
1265     bool DoBlockCheckHeaderListOnly) {
1266   return new PreprocessorTrackerImpl(Headers, DoBlockCheckHeaderListOnly);
1267 }
1268 
1269 // Preprocessor callbacks for modularize.
1270 
1271 // Handle include directive.
InclusionDirective(clang::SourceLocation HashLoc,const clang::Token & IncludeTok,llvm::StringRef FileName,bool IsAngled,clang::CharSourceRange FilenameRange,clang::OptionalFileEntryRef File,llvm::StringRef SearchPath,llvm::StringRef RelativePath,const clang::Module * SuggestedModule,bool ModuleImported,clang::SrcMgr::CharacteristicKind FileType)1272 void PreprocessorCallbacks::InclusionDirective(
1273     clang::SourceLocation HashLoc, const clang::Token &IncludeTok,
1274     llvm::StringRef FileName, bool IsAngled,
1275     clang::CharSourceRange FilenameRange, clang::OptionalFileEntryRef File,
1276     llvm::StringRef SearchPath, llvm::StringRef RelativePath,
1277     const clang::Module *SuggestedModule, bool ModuleImported,
1278     clang::SrcMgr::CharacteristicKind FileType) {
1279   int DirectiveLine, DirectiveColumn;
1280   std::string HeaderPath = getSourceLocationFile(PP, HashLoc);
1281   getSourceLocationLineAndColumn(PP, HashLoc, DirectiveLine, DirectiveColumn);
1282   PPTracker.handleIncludeDirective(HeaderPath, DirectiveLine, DirectiveColumn,
1283                                    FileName);
1284 }
1285 
1286 // Handle file entry/exit.
FileChanged(clang::SourceLocation Loc,clang::PPCallbacks::FileChangeReason Reason,clang::SrcMgr::CharacteristicKind FileType,clang::FileID PrevFID)1287 void PreprocessorCallbacks::FileChanged(
1288     clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason,
1289     clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) {
1290   switch (Reason) {
1291   case EnterFile:
1292     PPTracker.handleHeaderEntry(PP, getSourceLocationFile(PP, Loc));
1293     break;
1294   case ExitFile: {
1295     clang::OptionalFileEntryRef F =
1296         PP.getSourceManager().getFileEntryRefForID(PrevFID);
1297     if (F)
1298       PPTracker.handleHeaderExit(F->getName());
1299   } break;
1300   case SystemHeaderPragma:
1301   case RenameFile:
1302     break;
1303   }
1304 }
1305 
1306 // Handle macro expansion.
MacroExpands(const clang::Token & MacroNameTok,const clang::MacroDefinition & MD,clang::SourceRange Range,const clang::MacroArgs * Args)1307 void PreprocessorCallbacks::MacroExpands(const clang::Token &MacroNameTok,
1308                                          const clang::MacroDefinition &MD,
1309                                          clang::SourceRange Range,
1310                                          const clang::MacroArgs *Args) {
1311   clang::SourceLocation Loc = Range.getBegin();
1312   // Ignore macro argument expansions.
1313   if (!Loc.isFileID())
1314     return;
1315   clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
1316   const clang::MacroInfo *MI = MD.getMacroInfo();
1317   std::string MacroName = II->getName().str();
1318   std::string Unexpanded(getMacroUnexpandedString(Range, PP, MacroName, MI));
1319   std::string Expanded(getMacroExpandedString(PP, MacroName, MI, Args));
1320   PPTracker.addMacroExpansionInstance(
1321       PP, PPTracker.getCurrentHeaderHandle(), Loc, MI->getDefinitionLoc(), II,
1322       Unexpanded, Expanded, PPTracker.getCurrentInclusionPathHandle());
1323 }
1324 
Defined(const clang::Token & MacroNameTok,const clang::MacroDefinition & MD,clang::SourceRange Range)1325 void PreprocessorCallbacks::Defined(const clang::Token &MacroNameTok,
1326                                     const clang::MacroDefinition &MD,
1327                                     clang::SourceRange Range) {
1328   clang::SourceLocation Loc(Range.getBegin());
1329   clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
1330   const clang::MacroInfo *MI = MD.getMacroInfo();
1331   std::string MacroName = II->getName().str();
1332   std::string Unexpanded(getSourceString(PP, Range));
1333   PPTracker.addMacroExpansionInstance(
1334       PP, PPTracker.getCurrentHeaderHandle(), Loc,
1335       (MI ? MI->getDefinitionLoc() : Loc), II, Unexpanded,
1336       (MI ? "true" : "false"), PPTracker.getCurrentInclusionPathHandle());
1337 }
1338 
If(clang::SourceLocation Loc,clang::SourceRange ConditionRange,clang::PPCallbacks::ConditionValueKind ConditionResult)1339 void PreprocessorCallbacks::If(clang::SourceLocation Loc,
1340                                clang::SourceRange ConditionRange,
1341                                clang::PPCallbacks::ConditionValueKind ConditionResult) {
1342   std::string Unexpanded(getSourceString(PP, ConditionRange));
1343   PPTracker.addConditionalExpansionInstance(
1344       PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_if,
1345       ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());
1346 }
1347 
Elif(clang::SourceLocation Loc,clang::SourceRange ConditionRange,clang::PPCallbacks::ConditionValueKind ConditionResult,clang::SourceLocation IfLoc)1348 void PreprocessorCallbacks::Elif(clang::SourceLocation Loc,
1349                                  clang::SourceRange ConditionRange,
1350                                  clang::PPCallbacks::ConditionValueKind ConditionResult,
1351                                  clang::SourceLocation IfLoc) {
1352   std::string Unexpanded(getSourceString(PP, ConditionRange));
1353   PPTracker.addConditionalExpansionInstance(
1354       PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_elif,
1355       ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());
1356 }
1357 
Ifdef(clang::SourceLocation Loc,const clang::Token & MacroNameTok,const clang::MacroDefinition & MD)1358 void PreprocessorCallbacks::Ifdef(clang::SourceLocation Loc,
1359                                   const clang::Token &MacroNameTok,
1360                                   const clang::MacroDefinition &MD) {
1361   clang::PPCallbacks::ConditionValueKind IsDefined =
1362     (MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
1363   PPTracker.addConditionalExpansionInstance(
1364       PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifdef,
1365       IsDefined, PP.getSpelling(MacroNameTok),
1366       PPTracker.getCurrentInclusionPathHandle());
1367 }
1368 
Ifndef(clang::SourceLocation Loc,const clang::Token & MacroNameTok,const clang::MacroDefinition & MD)1369 void PreprocessorCallbacks::Ifndef(clang::SourceLocation Loc,
1370                                    const clang::Token &MacroNameTok,
1371                                    const clang::MacroDefinition &MD) {
1372   clang::PPCallbacks::ConditionValueKind IsNotDefined =
1373     (!MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
1374   PPTracker.addConditionalExpansionInstance(
1375       PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifndef,
1376       IsNotDefined, PP.getSpelling(MacroNameTok),
1377       PPTracker.getCurrentInclusionPathHandle());
1378 }
1379 } // end namespace Modularize
1380