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 const llvm::MemoryBuffer *MemBuffer =
316 PP.getSourceManager().getBuffer(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 const llvm::MemoryBuffer *MemBuffer = PP.getSourceManager().getBuffer(FileID);
342 const char *Buffer = MemBuffer->getBufferStart();
343 const char *BufferEnd = MemBuffer->getBufferEnd();
344 const char *BeginPtr = Buffer;
345 const char *EndPtr = BufferEnd;
346 int LineCounter = 1;
347 if (Line == 1)
348 BeginPtr = Buffer;
349 else {
350 while (Buffer < BufferEnd) {
351 if (*Buffer == '\n') {
352 if (++LineCounter == Line) {
353 BeginPtr = Buffer++ + 1;
354 break;
355 }
356 }
357 Buffer++;
358 }
359 }
360 while (Buffer < BufferEnd) {
361 if (*Buffer == '\n') {
362 EndPtr = Buffer;
363 break;
364 }
365 Buffer++;
366 }
367 size_t Length = EndPtr - BeginPtr;
368 return llvm::StringRef(BeginPtr, Length).str();
369 }
370
371 // Get the string for the Unexpanded macro instance.
372 // The sourceRange is expected to end at the last token
373 // for the macro instance, which in the case of a function-style
374 // macro will be a ')', but for an object-style macro, it
375 // will be the macro name itself.
getMacroUnexpandedString(clang::SourceRange Range,clang::Preprocessor & PP,llvm::StringRef MacroName,const clang::MacroInfo * MI)376 static std::string getMacroUnexpandedString(clang::SourceRange Range,
377 clang::Preprocessor &PP,
378 llvm::StringRef MacroName,
379 const clang::MacroInfo *MI) {
380 clang::SourceLocation BeginLoc(Range.getBegin());
381 const char *BeginPtr = PP.getSourceManager().getCharacterData(BeginLoc);
382 size_t Length;
383 std::string Unexpanded;
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 } else
389 Length = MacroName.size();
390 return llvm::StringRef(BeginPtr, Length).trim().str();
391 }
392
393 // Get the expansion for a macro instance, given the information
394 // provided by PPCallbacks.
395 // FIXME: This doesn't support function-style macro instances
396 // passed as arguments to another function-style macro. However,
397 // since it still expands the inner arguments, it still
398 // allows modularize to effectively work with respect to macro
399 // consistency checking, although it displays the incorrect
400 // expansion in error messages.
getMacroExpandedString(clang::Preprocessor & PP,llvm::StringRef MacroName,const clang::MacroInfo * MI,const clang::MacroArgs * Args)401 static 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 else
422 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 else
433 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 else
451 Expanded += Name;
452 }
453 }
454 }
455 return Expanded;
456 }
457
458 namespace {
459
460 // ConditionValueKind strings.
461 const char *
462 ConditionValueKindStrings[] = {
463 "(not evaluated)", "false", "true"
464 };
465
466 // Preprocessor item key.
467 //
468 // This class represents a location in a source file, for use
469 // 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.
474 class PPItemKey {
475 public:
PPItemKey(clang::Preprocessor & PP,StringHandle Name,HeaderHandle File,clang::SourceLocation Loc)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 }
PPItemKey(StringHandle Name,HeaderHandle File,int Line,int Column)481 PPItemKey(StringHandle Name, HeaderHandle File, int Line, int Column)
482 : Name(Name), File(File), Line(Line), Column(Column) {}
PPItemKey(const PPItemKey & Other)483 PPItemKey(const PPItemKey &Other)
484 : Name(Other.Name), File(Other.File), Line(Other.Line),
485 Column(Other.Column) {}
PPItemKey()486 PPItemKey() : File(HeaderHandleInvalid), Line(0), Column(0) {}
operator ==(const PPItemKey & Other) const487 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 }
operator <(const PPItemKey & Other) const496 bool operator<(const PPItemKey &Other) const {
497 if (Name < Other.Name)
498 return true;
499 else if (Name > Other.Name)
500 return false;
501 if (File < Other.File)
502 return true;
503 else if (File > Other.File)
504 return false;
505 if (Line < Other.Line)
506 return true;
507 else if (Line > Other.Line)
508 return false;
509 return Column < Other.Column;
510 }
511 StringHandle Name;
512 HeaderHandle File;
513 int Line;
514 int Column;
515 };
516
517 // Header inclusion path.
518 class HeaderInclusionPath {
519 public:
HeaderInclusionPath(std::vector<HeaderHandle> HeaderInclusionPath)520 HeaderInclusionPath(std::vector<HeaderHandle> HeaderInclusionPath)
521 : Path(HeaderInclusionPath) {}
HeaderInclusionPath(const HeaderInclusionPath & Other)522 HeaderInclusionPath(const HeaderInclusionPath &Other) : Path(Other.Path) {}
HeaderInclusionPath()523 HeaderInclusionPath() {}
524 std::vector<HeaderHandle> Path;
525 };
526
527 // Macro expansion instance.
528 //
529 // This class represents an instance of a macro expansion with a
530 // unique value. It also stores the unique header inclusion paths
531 // for use in telling the user the nested include path to the header.
532 class MacroExpansionInstance {
533 public:
MacroExpansionInstance(StringHandle MacroExpanded,PPItemKey & DefinitionLocation,StringHandle DefinitionSourceLine,InclusionPathHandle H)534 MacroExpansionInstance(StringHandle MacroExpanded,
535 PPItemKey &DefinitionLocation,
536 StringHandle DefinitionSourceLine,
537 InclusionPathHandle H)
538 : MacroExpanded(MacroExpanded), DefinitionLocation(DefinitionLocation),
539 DefinitionSourceLine(DefinitionSourceLine) {
540 InclusionPathHandles.push_back(H);
541 }
MacroExpansionInstance()542 MacroExpansionInstance() {}
543
544 // Check for the presence of a header inclusion path handle entry.
545 // Return false if not found.
haveInclusionPathHandle(InclusionPathHandle H)546 bool haveInclusionPathHandle(InclusionPathHandle H) {
547 for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
548 I != E; ++I) {
549 if (*I == H)
550 return true;
551 }
552 return InclusionPathHandleInvalid;
553 }
554 // Add a new header inclusion path entry, if not already present.
addInclusionPathHandle(InclusionPathHandle H)555 void addInclusionPathHandle(InclusionPathHandle H) {
556 if (!haveInclusionPathHandle(H))
557 InclusionPathHandles.push_back(H);
558 }
559
560 // A string representing the macro instance after preprocessing.
561 StringHandle MacroExpanded;
562 // A file/line/column triplet representing the macro definition location.
563 PPItemKey DefinitionLocation;
564 // A place to save the macro definition line string.
565 StringHandle DefinitionSourceLine;
566 // The header inclusion path handles for all the instances.
567 std::vector<InclusionPathHandle> InclusionPathHandles;
568 };
569
570 // Macro expansion instance tracker.
571 //
572 // This class represents one macro expansion, keyed by a PPItemKey.
573 // It stores a string representing the macro reference in the source,
574 // and a list of ConditionalExpansionInstances objects representing
575 // the unique values the condition expands to in instances of the header.
576 class MacroExpansionTracker {
577 public:
MacroExpansionTracker(StringHandle MacroUnexpanded,StringHandle MacroExpanded,StringHandle InstanceSourceLine,PPItemKey & DefinitionLocation,StringHandle DefinitionSourceLine,InclusionPathHandle InclusionPathHandle)578 MacroExpansionTracker(StringHandle MacroUnexpanded,
579 StringHandle MacroExpanded,
580 StringHandle InstanceSourceLine,
581 PPItemKey &DefinitionLocation,
582 StringHandle DefinitionSourceLine,
583 InclusionPathHandle InclusionPathHandle)
584 : MacroUnexpanded(MacroUnexpanded),
585 InstanceSourceLine(InstanceSourceLine) {
586 addMacroExpansionInstance(MacroExpanded, DefinitionLocation,
587 DefinitionSourceLine, InclusionPathHandle);
588 }
MacroExpansionTracker()589 MacroExpansionTracker() {}
590
591 // Find a matching macro expansion instance.
592 MacroExpansionInstance *
findMacroExpansionInstance(StringHandle MacroExpanded,PPItemKey & DefinitionLocation)593 findMacroExpansionInstance(StringHandle MacroExpanded,
594 PPItemKey &DefinitionLocation) {
595 for (auto I = MacroExpansionInstances.begin(),
596 E = MacroExpansionInstances.end();
597 I != E; ++I) {
598 if ((I->MacroExpanded == MacroExpanded) &&
599 (I->DefinitionLocation == DefinitionLocation)) {
600 return &*I; // Found.
601 }
602 }
603 return nullptr; // Not found.
604 }
605
606 // Add a macro expansion instance.
addMacroExpansionInstance(StringHandle MacroExpanded,PPItemKey & DefinitionLocation,StringHandle DefinitionSourceLine,InclusionPathHandle InclusionPathHandle)607 void addMacroExpansionInstance(StringHandle MacroExpanded,
608 PPItemKey &DefinitionLocation,
609 StringHandle DefinitionSourceLine,
610 InclusionPathHandle InclusionPathHandle) {
611 MacroExpansionInstances.push_back(
612 MacroExpansionInstance(MacroExpanded, DefinitionLocation,
613 DefinitionSourceLine, InclusionPathHandle));
614 }
615
616 // Return true if there is a mismatch.
hasMismatch()617 bool hasMismatch() { return MacroExpansionInstances.size() > 1; }
618
619 // A string representing the macro instance without expansion.
620 StringHandle MacroUnexpanded;
621 // A place to save the macro instance source line string.
622 StringHandle InstanceSourceLine;
623 // The macro expansion instances.
624 // If all instances of the macro expansion expand to the same value,
625 // This vector will only have one instance.
626 std::vector<MacroExpansionInstance> MacroExpansionInstances;
627 };
628
629 // Conditional expansion instance.
630 //
631 // This class represents an instance of a condition exoression result
632 // with a unique value. It also stores the unique header inclusion paths
633 // for use in telling the user the nested include path to the header.
634 class ConditionalExpansionInstance {
635 public:
ConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue,InclusionPathHandle H)636 ConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue, InclusionPathHandle H)
637 : ConditionValue(ConditionValue) {
638 InclusionPathHandles.push_back(H);
639 }
ConditionalExpansionInstance()640 ConditionalExpansionInstance() {}
641
642 // Check for the presence of a header inclusion path handle entry.
643 // Return false if not found.
haveInclusionPathHandle(InclusionPathHandle H)644 bool haveInclusionPathHandle(InclusionPathHandle H) {
645 for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
646 I != E; ++I) {
647 if (*I == H)
648 return true;
649 }
650 return InclusionPathHandleInvalid;
651 }
652 // Add a new header inclusion path entry, if not already present.
addInclusionPathHandle(InclusionPathHandle H)653 void addInclusionPathHandle(InclusionPathHandle H) {
654 if (!haveInclusionPathHandle(H))
655 InclusionPathHandles.push_back(H);
656 }
657
658 // A flag representing the evaluated condition value.
659 clang::PPCallbacks::ConditionValueKind ConditionValue;
660 // The header inclusion path handles for all the instances.
661 std::vector<InclusionPathHandle> InclusionPathHandles;
662 };
663
664 // Conditional directive instance tracker.
665 //
666 // This class represents one conditional directive, keyed by a PPItemKey.
667 // It stores a string representing the macro reference in the source,
668 // and a list of ConditionExpansionInstance objects representing
669 // the unique value the condition expression expands to in instances of
670 // the header.
671 class ConditionalTracker {
672 public:
ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind,clang::PPCallbacks::ConditionValueKind ConditionValue,StringHandle ConditionUnexpanded,InclusionPathHandle InclusionPathHandle)673 ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind,
674 clang::PPCallbacks::ConditionValueKind ConditionValue,
675 StringHandle ConditionUnexpanded,
676 InclusionPathHandle InclusionPathHandle)
677 : DirectiveKind(DirectiveKind), ConditionUnexpanded(ConditionUnexpanded) {
678 addConditionalExpansionInstance(ConditionValue, InclusionPathHandle);
679 }
ConditionalTracker()680 ConditionalTracker() {}
681
682 // Find a matching condition expansion instance.
683 ConditionalExpansionInstance *
findConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue)684 findConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue) {
685 for (auto I = ConditionalExpansionInstances.begin(),
686 E = ConditionalExpansionInstances.end();
687 I != E; ++I) {
688 if (I->ConditionValue == ConditionValue) {
689 return &*I; // Found.
690 }
691 }
692 return nullptr; // Not found.
693 }
694
695 // Add a conditional expansion instance.
696 void
addConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue,InclusionPathHandle InclusionPathHandle)697 addConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue,
698 InclusionPathHandle InclusionPathHandle) {
699 ConditionalExpansionInstances.push_back(
700 ConditionalExpansionInstance(ConditionValue, InclusionPathHandle));
701 }
702
703 // Return true if there is a mismatch.
hasMismatch()704 bool hasMismatch() { return ConditionalExpansionInstances.size() > 1; }
705
706 // The kind of directive.
707 clang::tok::PPKeywordKind DirectiveKind;
708 // A string representing the macro instance without expansion.
709 StringHandle ConditionUnexpanded;
710 // The condition expansion instances.
711 // If all instances of the conditional expression expand to the same value,
712 // This vector will only have one instance.
713 std::vector<ConditionalExpansionInstance> ConditionalExpansionInstances;
714 };
715
716 class PreprocessorTrackerImpl;
717
718 // Preprocessor callbacks for modularize.
719 //
720 // This class derives from the Clang PPCallbacks class to track preprocessor
721 // actions, such as changing files and handling preprocessor directives and
722 // macro expansions. It has to figure out when a new header file is entered
723 // and left, as the provided handler is not particularly clear about it.
724 class PreprocessorCallbacks : public clang::PPCallbacks {
725 public:
PreprocessorCallbacks(PreprocessorTrackerImpl & ppTracker,clang::Preprocessor & PP,llvm::StringRef rootHeaderFile)726 PreprocessorCallbacks(PreprocessorTrackerImpl &ppTracker,
727 clang::Preprocessor &PP, llvm::StringRef rootHeaderFile)
728 : PPTracker(ppTracker), PP(PP), RootHeaderFile(rootHeaderFile) {}
~PreprocessorCallbacks()729 ~PreprocessorCallbacks() override {}
730
731 // Overridden handlers.
732 void InclusionDirective(clang::SourceLocation HashLoc,
733 const clang::Token &IncludeTok,
734 llvm::StringRef FileName, bool IsAngled,
735 clang::CharSourceRange FilenameRange,
736 const clang::FileEntry *File,
737 llvm::StringRef SearchPath,
738 llvm::StringRef RelativePath,
739 const clang::Module *Imported,
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.startswith("<"))
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.startswith("<"))
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 for (auto I = HeaderStack.begin(), E = HeaderStack.end(); I != E; ++I) {
990 if (*I == H)
991 return true;
992 }
993 return false;
994 }
995
996 // Get the handle of a header inclusion path entry.
997 // Return InclusionPathHandleInvalid if not found.
998 InclusionPathHandle
findInclusionPathHandle(const std::vector<HeaderHandle> & Path) const999 findInclusionPathHandle(const std::vector<HeaderHandle> &Path) const {
1000 InclusionPathHandle H = 0;
1001 for (auto I = InclusionPaths.begin(), E = InclusionPaths.end(); I != E;
1002 ++I, ++H) {
1003 if (I->Path == Path)
1004 return H;
1005 }
1006 return HeaderHandleInvalid;
1007 }
1008 // Add a new header inclusion path entry, or return existing handle.
1009 // Return the header inclusion path entry handle.
1010 InclusionPathHandle
addInclusionPathHandle(const std::vector<HeaderHandle> & Path)1011 addInclusionPathHandle(const std::vector<HeaderHandle> &Path) {
1012 InclusionPathHandle H = findInclusionPathHandle(Path);
1013 if (H == HeaderHandleInvalid) {
1014 H = InclusionPaths.size();
1015 InclusionPaths.push_back(HeaderInclusionPath(Path));
1016 }
1017 return H;
1018 }
1019 // Return the current inclusion path handle.
getCurrentInclusionPathHandle() const1020 InclusionPathHandle getCurrentInclusionPathHandle() const {
1021 return CurrentInclusionPathHandle;
1022 }
1023
1024 // Return an inclusion path given its handle.
1025 const std::vector<HeaderHandle> &
getInclusionPath(InclusionPathHandle H) const1026 getInclusionPath(InclusionPathHandle H) const {
1027 if ((H >= 0) && (H <= (InclusionPathHandle)InclusionPaths.size()))
1028 return InclusionPaths[H].Path;
1029 static std::vector<HeaderHandle> Empty;
1030 return Empty;
1031 }
1032
1033 // 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)1034 void addMacroExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
1035 clang::SourceLocation InstanceLoc,
1036 clang::SourceLocation DefinitionLoc,
1037 clang::IdentifierInfo *II,
1038 llvm::StringRef MacroUnexpanded,
1039 llvm::StringRef MacroExpanded,
1040 InclusionPathHandle InclusionPathHandle) {
1041 if (InNestedHeader)
1042 return;
1043 StringHandle MacroName = addString(II->getName());
1044 PPItemKey InstanceKey(PP, MacroName, H, InstanceLoc);
1045 PPItemKey DefinitionKey(PP, MacroName, H, DefinitionLoc);
1046 auto I = MacroExpansions.find(InstanceKey);
1047 // If existing instance of expansion not found, add one.
1048 if (I == MacroExpansions.end()) {
1049 std::string InstanceSourceLine =
1050 getSourceLocationString(PP, InstanceLoc) + ":\n" +
1051 getSourceLine(PP, InstanceLoc) + "\n";
1052 std::string DefinitionSourceLine =
1053 getSourceLocationString(PP, DefinitionLoc) + ":\n" +
1054 getSourceLine(PP, DefinitionLoc) + "\n";
1055 MacroExpansions[InstanceKey] = MacroExpansionTracker(
1056 addString(MacroUnexpanded), addString(MacroExpanded),
1057 addString(InstanceSourceLine), DefinitionKey,
1058 addString(DefinitionSourceLine), InclusionPathHandle);
1059 } else {
1060 // We've seen the macro before. Get its tracker.
1061 MacroExpansionTracker &CondTracker = I->second;
1062 // Look up an existing instance value for the macro.
1063 MacroExpansionInstance *MacroInfo =
1064 CondTracker.findMacroExpansionInstance(addString(MacroExpanded),
1065 DefinitionKey);
1066 // If found, just add the inclusion path to the instance.
1067 if (MacroInfo)
1068 MacroInfo->addInclusionPathHandle(InclusionPathHandle);
1069 else {
1070 // Otherwise add a new instance with the unique value.
1071 std::string DefinitionSourceLine =
1072 getSourceLocationString(PP, DefinitionLoc) + ":\n" +
1073 getSourceLine(PP, DefinitionLoc) + "\n";
1074 CondTracker.addMacroExpansionInstance(
1075 addString(MacroExpanded), DefinitionKey,
1076 addString(DefinitionSourceLine), InclusionPathHandle);
1077 }
1078 }
1079 }
1080
1081 // Add a conditional expansion instance.
1082 void
addConditionalExpansionInstance(clang::Preprocessor & PP,HeaderHandle H,clang::SourceLocation InstanceLoc,clang::tok::PPKeywordKind DirectiveKind,clang::PPCallbacks::ConditionValueKind ConditionValue,llvm::StringRef ConditionUnexpanded,InclusionPathHandle InclusionPathHandle)1083 addConditionalExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
1084 clang::SourceLocation InstanceLoc,
1085 clang::tok::PPKeywordKind DirectiveKind,
1086 clang::PPCallbacks::ConditionValueKind ConditionValue,
1087 llvm::StringRef ConditionUnexpanded,
1088 InclusionPathHandle InclusionPathHandle) {
1089 // Ignore header guards, assuming the header guard is the only conditional.
1090 if (InNestedHeader)
1091 return;
1092 StringHandle ConditionUnexpandedHandle(addString(ConditionUnexpanded));
1093 PPItemKey InstanceKey(PP, ConditionUnexpandedHandle, H, InstanceLoc);
1094 auto I = ConditionalExpansions.find(InstanceKey);
1095 // If existing instance of condition not found, add one.
1096 if (I == ConditionalExpansions.end()) {
1097 std::string InstanceSourceLine =
1098 getSourceLocationString(PP, InstanceLoc) + ":\n" +
1099 getSourceLine(PP, InstanceLoc) + "\n";
1100 ConditionalExpansions[InstanceKey] =
1101 ConditionalTracker(DirectiveKind, ConditionValue,
1102 ConditionUnexpandedHandle, InclusionPathHandle);
1103 } else {
1104 // We've seen the conditional before. Get its tracker.
1105 ConditionalTracker &CondTracker = I->second;
1106 // Look up an existing instance value for the condition.
1107 ConditionalExpansionInstance *MacroInfo =
1108 CondTracker.findConditionalExpansionInstance(ConditionValue);
1109 // If found, just add the inclusion path to the instance.
1110 if (MacroInfo)
1111 MacroInfo->addInclusionPathHandle(InclusionPathHandle);
1112 else {
1113 // Otherwise add a new instance with the unique value.
1114 CondTracker.addConditionalExpansionInstance(ConditionValue,
1115 InclusionPathHandle);
1116 }
1117 }
1118 }
1119
1120 // Report on inconsistent macro instances.
1121 // Returns true if any mismatches.
reportInconsistentMacros(llvm::raw_ostream & OS)1122 bool reportInconsistentMacros(llvm::raw_ostream &OS) override {
1123 bool ReturnValue = false;
1124 // Walk all the macro expansion trackers in the map.
1125 for (auto I = MacroExpansions.begin(), E = MacroExpansions.end(); I != E;
1126 ++I) {
1127 const PPItemKey &ItemKey = I->first;
1128 MacroExpansionTracker &MacroExpTracker = I->second;
1129 // If no mismatch (only one instance value) continue.
1130 if (!MacroExpTracker.hasMismatch())
1131 continue;
1132 // Tell caller we found one or more errors.
1133 ReturnValue = true;
1134 // Start the error message.
1135 OS << MacroExpTracker.InstanceSourceLine;
1136 if (ItemKey.Column > 0)
1137 OS << std::string(ItemKey.Column - 1, ' ') << "^\n";
1138 OS << "error: Macro instance '" << MacroExpTracker.MacroUnexpanded
1139 << "' has different values in this header, depending on how it was "
1140 "included.\n";
1141 // Walk all the instances.
1142 for (auto IMT = MacroExpTracker.MacroExpansionInstances.begin(),
1143 EMT = MacroExpTracker.MacroExpansionInstances.end();
1144 IMT != EMT; ++IMT) {
1145 MacroExpansionInstance &MacroInfo = *IMT;
1146 OS << " '" << MacroExpTracker.MacroUnexpanded << "' expanded to: '"
1147 << MacroInfo.MacroExpanded
1148 << "' with respect to these inclusion paths:\n";
1149 // Walk all the inclusion path hierarchies.
1150 for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
1151 EIP = MacroInfo.InclusionPathHandles.end();
1152 IIP != EIP; ++IIP) {
1153 const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
1154 auto Count = (int)ip.size();
1155 for (int Index = 0; Index < Count; ++Index) {
1156 HeaderHandle H = ip[Index];
1157 OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H)
1158 << "\n";
1159 }
1160 }
1161 // For a macro that wasn't defined, we flag it by using the
1162 // instance location.
1163 // If there is a definition...
1164 if (MacroInfo.DefinitionLocation.Line != ItemKey.Line) {
1165 OS << MacroInfo.DefinitionSourceLine;
1166 if (MacroInfo.DefinitionLocation.Column > 0)
1167 OS << std::string(MacroInfo.DefinitionLocation.Column - 1, ' ')
1168 << "^\n";
1169 OS << "Macro defined here.\n";
1170 } else
1171 OS << "(no macro definition)"
1172 << "\n";
1173 }
1174 }
1175 return ReturnValue;
1176 }
1177
1178 // Report on inconsistent conditional instances.
1179 // Returns true if any mismatches.
reportInconsistentConditionals(llvm::raw_ostream & OS)1180 bool reportInconsistentConditionals(llvm::raw_ostream &OS) override {
1181 bool ReturnValue = false;
1182 // Walk all the conditional trackers in the map.
1183 for (auto I = ConditionalExpansions.begin(),
1184 E = ConditionalExpansions.end();
1185 I != E; ++I) {
1186 const PPItemKey &ItemKey = I->first;
1187 ConditionalTracker &CondTracker = I->second;
1188 if (!CondTracker.hasMismatch())
1189 continue;
1190 // Tell caller we found one or more errors.
1191 ReturnValue = true;
1192 // Start the error message.
1193 OS << HeaderPaths[ItemKey.File] << ":" << ItemKey.Line << ":"
1194 << ItemKey.Column << "\n";
1195 OS << "#" << getDirectiveSpelling(CondTracker.DirectiveKind) << " "
1196 << CondTracker.ConditionUnexpanded << "\n";
1197 OS << "^\n";
1198 OS << "error: Conditional expression instance '"
1199 << CondTracker.ConditionUnexpanded
1200 << "' has different values in this header, depending on how it was "
1201 "included.\n";
1202 // Walk all the instances.
1203 for (auto IMT = CondTracker.ConditionalExpansionInstances.begin(),
1204 EMT = CondTracker.ConditionalExpansionInstances.end();
1205 IMT != EMT; ++IMT) {
1206 ConditionalExpansionInstance &MacroInfo = *IMT;
1207 OS << " '" << CondTracker.ConditionUnexpanded << "' expanded to: '"
1208 << ConditionValueKindStrings[MacroInfo.ConditionValue]
1209 << "' with respect to these inclusion paths:\n";
1210 // Walk all the inclusion path hierarchies.
1211 for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
1212 EIP = MacroInfo.InclusionPathHandles.end();
1213 IIP != EIP; ++IIP) {
1214 const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
1215 auto Count = (int)ip.size();
1216 for (int Index = 0; Index < Count; ++Index) {
1217 HeaderHandle H = ip[Index];
1218 OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H)
1219 << "\n";
1220 }
1221 }
1222 }
1223 }
1224 return ReturnValue;
1225 }
1226
1227 // Get directive spelling.
getDirectiveSpelling(clang::tok::PPKeywordKind kind)1228 static const char *getDirectiveSpelling(clang::tok::PPKeywordKind kind) {
1229 switch (kind) {
1230 case clang::tok::pp_if:
1231 return "if";
1232 case clang::tok::pp_elif:
1233 return "elif";
1234 case clang::tok::pp_ifdef:
1235 return "ifdef";
1236 case clang::tok::pp_ifndef:
1237 return "ifndef";
1238 default:
1239 return "(unknown)";
1240 }
1241 }
1242
1243 private:
1244 llvm::SmallVector<std::string, 32> HeaderList;
1245 // Only do extern, namespace check for headers in HeaderList.
1246 bool BlockCheckHeaderListOnly;
1247 llvm::StringSet<> Strings;
1248 std::vector<StringHandle> HeaderPaths;
1249 std::vector<HeaderHandle> HeaderStack;
1250 std::vector<HeaderInclusionPath> InclusionPaths;
1251 InclusionPathHandle CurrentInclusionPathHandle;
1252 llvm::SmallSet<HeaderHandle, 32> HeadersInThisCompile;
1253 std::vector<PPItemKey> IncludeDirectives;
1254 MacroExpansionMap MacroExpansions;
1255 ConditionalExpansionMap ConditionalExpansions;
1256 bool InNestedHeader;
1257 };
1258
1259 } // namespace
1260
1261 // PreprocessorTracker functions.
1262
1263 // PreprocessorTracker destructor.
~PreprocessorTracker()1264 PreprocessorTracker::~PreprocessorTracker() {}
1265
1266 // Create instance of PreprocessorTracker.
create(llvm::SmallVector<std::string,32> & Headers,bool DoBlockCheckHeaderListOnly)1267 PreprocessorTracker *PreprocessorTracker::create(
1268 llvm::SmallVector<std::string, 32> &Headers,
1269 bool DoBlockCheckHeaderListOnly) {
1270 return new PreprocessorTrackerImpl(Headers, DoBlockCheckHeaderListOnly);
1271 }
1272
1273 // Preprocessor callbacks for modularize.
1274
1275 // Handle include directive.
InclusionDirective(clang::SourceLocation HashLoc,const clang::Token & IncludeTok,llvm::StringRef FileName,bool IsAngled,clang::CharSourceRange FilenameRange,const clang::FileEntry * File,llvm::StringRef SearchPath,llvm::StringRef RelativePath,const clang::Module * Imported,clang::SrcMgr::CharacteristicKind FileType)1276 void PreprocessorCallbacks::InclusionDirective(
1277 clang::SourceLocation HashLoc, const clang::Token &IncludeTok,
1278 llvm::StringRef FileName, bool IsAngled,
1279 clang::CharSourceRange FilenameRange, const clang::FileEntry *File,
1280 llvm::StringRef SearchPath, llvm::StringRef RelativePath,
1281 const clang::Module *Imported, clang::SrcMgr::CharacteristicKind FileType) {
1282 int DirectiveLine, DirectiveColumn;
1283 std::string HeaderPath = getSourceLocationFile(PP, HashLoc);
1284 getSourceLocationLineAndColumn(PP, HashLoc, DirectiveLine, DirectiveColumn);
1285 PPTracker.handleIncludeDirective(HeaderPath, DirectiveLine, DirectiveColumn,
1286 FileName);
1287 }
1288
1289 // Handle file entry/exit.
FileChanged(clang::SourceLocation Loc,clang::PPCallbacks::FileChangeReason Reason,clang::SrcMgr::CharacteristicKind FileType,clang::FileID PrevFID)1290 void PreprocessorCallbacks::FileChanged(
1291 clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason,
1292 clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) {
1293 switch (Reason) {
1294 case EnterFile:
1295 PPTracker.handleHeaderEntry(PP, getSourceLocationFile(PP, Loc));
1296 break;
1297 case ExitFile: {
1298 const clang::FileEntry *F =
1299 PP.getSourceManager().getFileEntryForID(PrevFID);
1300 if (F)
1301 PPTracker.handleHeaderExit(F->getName());
1302 } break;
1303 case SystemHeaderPragma:
1304 case RenameFile:
1305 break;
1306 }
1307 }
1308
1309 // Handle macro expansion.
MacroExpands(const clang::Token & MacroNameTok,const clang::MacroDefinition & MD,clang::SourceRange Range,const clang::MacroArgs * Args)1310 void PreprocessorCallbacks::MacroExpands(const clang::Token &MacroNameTok,
1311 const clang::MacroDefinition &MD,
1312 clang::SourceRange Range,
1313 const clang::MacroArgs *Args) {
1314 clang::SourceLocation Loc = Range.getBegin();
1315 // Ignore macro argument expansions.
1316 if (!Loc.isFileID())
1317 return;
1318 clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
1319 const clang::MacroInfo *MI = MD.getMacroInfo();
1320 std::string MacroName = II->getName().str();
1321 std::string Unexpanded(getMacroUnexpandedString(Range, PP, MacroName, MI));
1322 std::string Expanded(getMacroExpandedString(PP, MacroName, MI, Args));
1323 PPTracker.addMacroExpansionInstance(
1324 PP, PPTracker.getCurrentHeaderHandle(), Loc, MI->getDefinitionLoc(), II,
1325 Unexpanded, Expanded, PPTracker.getCurrentInclusionPathHandle());
1326 }
1327
Defined(const clang::Token & MacroNameTok,const clang::MacroDefinition & MD,clang::SourceRange Range)1328 void PreprocessorCallbacks::Defined(const clang::Token &MacroNameTok,
1329 const clang::MacroDefinition &MD,
1330 clang::SourceRange Range) {
1331 clang::SourceLocation Loc(Range.getBegin());
1332 clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
1333 const clang::MacroInfo *MI = MD.getMacroInfo();
1334 std::string MacroName = II->getName().str();
1335 std::string Unexpanded(getSourceString(PP, Range));
1336 PPTracker.addMacroExpansionInstance(
1337 PP, PPTracker.getCurrentHeaderHandle(), Loc,
1338 (MI ? MI->getDefinitionLoc() : Loc), II, Unexpanded,
1339 (MI ? "true" : "false"), PPTracker.getCurrentInclusionPathHandle());
1340 }
1341
If(clang::SourceLocation Loc,clang::SourceRange ConditionRange,clang::PPCallbacks::ConditionValueKind ConditionResult)1342 void PreprocessorCallbacks::If(clang::SourceLocation Loc,
1343 clang::SourceRange ConditionRange,
1344 clang::PPCallbacks::ConditionValueKind ConditionResult) {
1345 std::string Unexpanded(getSourceString(PP, ConditionRange));
1346 PPTracker.addConditionalExpansionInstance(
1347 PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_if,
1348 ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());
1349 }
1350
Elif(clang::SourceLocation Loc,clang::SourceRange ConditionRange,clang::PPCallbacks::ConditionValueKind ConditionResult,clang::SourceLocation IfLoc)1351 void PreprocessorCallbacks::Elif(clang::SourceLocation Loc,
1352 clang::SourceRange ConditionRange,
1353 clang::PPCallbacks::ConditionValueKind ConditionResult,
1354 clang::SourceLocation IfLoc) {
1355 std::string Unexpanded(getSourceString(PP, ConditionRange));
1356 PPTracker.addConditionalExpansionInstance(
1357 PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_elif,
1358 ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());
1359 }
1360
Ifdef(clang::SourceLocation Loc,const clang::Token & MacroNameTok,const clang::MacroDefinition & MD)1361 void PreprocessorCallbacks::Ifdef(clang::SourceLocation Loc,
1362 const clang::Token &MacroNameTok,
1363 const clang::MacroDefinition &MD) {
1364 clang::PPCallbacks::ConditionValueKind IsDefined =
1365 (MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
1366 PPTracker.addConditionalExpansionInstance(
1367 PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifdef,
1368 IsDefined, PP.getSpelling(MacroNameTok),
1369 PPTracker.getCurrentInclusionPathHandle());
1370 }
1371
Ifndef(clang::SourceLocation Loc,const clang::Token & MacroNameTok,const clang::MacroDefinition & MD)1372 void PreprocessorCallbacks::Ifndef(clang::SourceLocation Loc,
1373 const clang::Token &MacroNameTok,
1374 const clang::MacroDefinition &MD) {
1375 clang::PPCallbacks::ConditionValueKind IsNotDefined =
1376 (!MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
1377 PPTracker.addConditionalExpansionInstance(
1378 PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifndef,
1379 IsNotDefined, PP.getSpelling(MacroNameTok),
1380 PPTracker.getCurrentInclusionPathHandle());
1381 }
1382 } // end namespace Modularize
1383