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
255namespace Modularize {
256
257// Some handle types
258typedef llvm::StringRef StringHandle;
259
260typedef int HeaderHandle;
261const HeaderHandle HeaderHandleInvalid = -1;
262
263typedef int InclusionPathHandle;
264const InclusionPathHandle InclusionPathHandleInvalid = -1;
265
266// Some utility functions.
267
268// Get a "file:line:column" source location string.
269static std::string getSourceLocationString(clang::Preprocessor &PP,
270 clang::SourceLocation Loc) {
271 if (Loc.isInvalid())
272 return std::string("(none)");
273 else
274 return Loc.printToString(SM: PP.getSourceManager());
275}
276
277// Get just the file name from a source location.
278static std::string getSourceLocationFile(clang::Preprocessor &PP,
279 clang::SourceLocation Loc) {
280 std::string Source(getSourceLocationString(PP, Loc));
281 size_t Offset = Source.find(c: ':', pos: 2);
282 if (Offset == std::string::npos)
283 return Source;
284 return Source.substr(pos: 0, n: Offset);
285}
286
287// Get just the line and column from a source location.
288static void getSourceLocationLineAndColumn(clang::Preprocessor &PP,
289 clang::SourceLocation Loc, int &Line,
290 int &Column) {
291 clang::PresumedLoc PLoc = PP.getSourceManager().getPresumedLoc(Loc);
292 if (PLoc.isInvalid()) {
293 Line = 0;
294 Column = 0;
295 return;
296 }
297 Line = PLoc.getLine();
298 Column = PLoc.getColumn();
299}
300
301// Retrieve source snippet from file image.
302static std::string getSourceString(clang::Preprocessor &PP,
303 clang::SourceRange Range) {
304 clang::SourceLocation BeginLoc = Range.getBegin();
305 clang::SourceLocation EndLoc = Range.getEnd();
306 const char *BeginPtr = PP.getSourceManager().getCharacterData(SL: BeginLoc);
307 const char *EndPtr = PP.getSourceManager().getCharacterData(SL: EndLoc);
308 size_t Length = EndPtr - BeginPtr;
309 return llvm::StringRef(BeginPtr, Length).trim().str();
310}
311
312// Retrieve source line from file image given a location.
313static std::string getSourceLine(clang::Preprocessor &PP,
314 clang::SourceLocation Loc) {
315 llvm::MemoryBufferRef MemBuffer = PP.getSourceManager().getBufferOrFake(
316 FID: PP.getSourceManager().getFileID(SpellingLoc: Loc));
317 const char *Buffer = MemBuffer.getBufferStart();
318 const char *BufferEnd = MemBuffer.getBufferEnd();
319 const char *BeginPtr = PP.getSourceManager().getCharacterData(SL: Loc);
320 const char *EndPtr = BeginPtr;
321 while (BeginPtr > Buffer) {
322 if (*BeginPtr == '\n') {
323 BeginPtr++;
324 break;
325 }
326 BeginPtr--;
327 }
328 while (EndPtr < BufferEnd) {
329 if (*EndPtr == '\n') {
330 break;
331 }
332 EndPtr++;
333 }
334 size_t Length = EndPtr - BeginPtr;
335 return llvm::StringRef(BeginPtr, Length).str();
336}
337
338// Retrieve source line from file image given a file ID and line number.
339static std::string getSourceLine(clang::Preprocessor &PP, clang::FileID FileID,
340 int Line) {
341 llvm::MemoryBufferRef MemBuffer =
342 PP.getSourceManager().getBufferOrFake(FID: 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.
377static std::string getMacroUnexpandedString(clang::SourceRange Range,
378 clang::Preprocessor &PP,
379 llvm::StringRef MacroName,
380 const clang::MacroInfo *MI) {
381 clang::SourceLocation BeginLoc(Range.getBegin());
382 const char *BeginPtr = PP.getSourceManager().getCharacterData(SL: BeginLoc);
383 size_t Length;
384 if (MI->isFunctionLike()) {
385 clang::SourceLocation EndLoc(Range.getEnd());
386 const char *EndPtr = PP.getSourceManager().getCharacterData(SL: 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.
401static std::string getMacroExpandedString(clang::Preprocessor &PP,
402 llvm::StringRef MacroName,
403 const clang::MacroInfo *MI,
404 const clang::MacroArgs *Args) {
405 std::string Expanded;
406 // Walk over the macro Tokens.
407 for (const auto &T : MI->tokens()) {
408 clang::IdentifierInfo *II = T.getIdentifierInfo();
409 int ArgNo = (II && Args ? MI->getParameterNum(Arg: II) : -1);
410 if (ArgNo == -1) {
411 // This isn't an argument, just add it.
412 if (II == nullptr)
413 Expanded += PP.getSpelling(Tok: 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, MacroName: Name, MI: MacroInfo, Args: 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(Arg: ArgNo);
429 if (Args->ArgNeedsPreexpansion(ArgTok, PP))
430 ResultArgToks = &(const_cast<clang::MacroArgs *>(Args))
431 ->getPreExpArgument(Arg: 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(K: clang::tok::eof))
436 continue;
437 unsigned NumToks = clang::MacroArgs::getArgLength(ArgPtr: 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(Tok: 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, MacroName: Name, MI: MacroInfo, Args: nullptr);
450 else
451 Expanded += Name;
452 }
453 }
454 }
455 return Expanded;
456}
457
458namespace {
459
460// ConditionValueKind strings.
461const char *
462ConditionValueKindStrings[] = {
463 "(not evaluated)", "false", "true"
464};
465
466// Preprocessor item key.
467//
468// This class represents a location in a source file, for 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.
474class PPItemKey {
475public:
476 PPItemKey(clang::Preprocessor &PP, StringHandle Name, HeaderHandle File,
477 clang::SourceLocation Loc)
478 : Name(Name), File(File) {
479 getSourceLocationLineAndColumn(PP, Loc, Line, Column);
480 }
481 PPItemKey(StringHandle Name, HeaderHandle File, int Line, int Column)
482 : Name(Name), File(File), Line(Line), Column(Column) {}
483 PPItemKey(const PPItemKey &Other)
484 : Name(Other.Name), File(Other.File), Line(Other.Line),
485 Column(Other.Column) {}
486 PPItemKey() : File(HeaderHandleInvalid), Line(0), Column(0) {}
487 bool operator==(const PPItemKey &Other) const {
488 if (Name != Other.Name)
489 return false;
490 if (File != Other.File)
491 return false;
492 if (Line != Other.Line)
493 return false;
494 return Column == Other.Column;
495 }
496 bool operator<(const PPItemKey &Other) const {
497 return std::tie(args: Name, args: File, args: Line, args: Column) <
498 std::tie(args: Other.Name, args: Other.File, args: Other.Line, args: Other.Column);
499 }
500 StringHandle Name;
501 HeaderHandle File;
502 int Line;
503 int Column;
504};
505
506// Header inclusion path.
507class HeaderInclusionPath {
508public:
509 HeaderInclusionPath(std::vector<HeaderHandle> HeaderInclusionPath)
510 : Path(HeaderInclusionPath) {}
511 HeaderInclusionPath(const HeaderInclusionPath &Other) : Path(Other.Path) {}
512 HeaderInclusionPath() {}
513 std::vector<HeaderHandle> Path;
514};
515
516// Macro expansion instance.
517//
518// This class represents an instance of a macro expansion with a
519// unique value. It also stores the unique header inclusion paths
520// for use in telling the user the nested include path to the header.
521class MacroExpansionInstance {
522public:
523 MacroExpansionInstance(StringHandle MacroExpanded,
524 PPItemKey &DefinitionLocation,
525 StringHandle DefinitionSourceLine,
526 InclusionPathHandle H)
527 : MacroExpanded(MacroExpanded), DefinitionLocation(DefinitionLocation),
528 DefinitionSourceLine(DefinitionSourceLine) {
529 InclusionPathHandles.push_back(x: H);
530 }
531 MacroExpansionInstance() {}
532
533 // Check for the presence of a header inclusion path handle entry.
534 // Return false if not found.
535 bool haveInclusionPathHandle(InclusionPathHandle H) {
536 for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
537 I != E; ++I) {
538 if (*I == H)
539 return true;
540 }
541 return InclusionPathHandleInvalid;
542 }
543 // Add a new header inclusion path entry, if not already present.
544 void addInclusionPathHandle(InclusionPathHandle H) {
545 if (!haveInclusionPathHandle(H))
546 InclusionPathHandles.push_back(x: H);
547 }
548
549 // A string representing the macro instance after preprocessing.
550 StringHandle MacroExpanded;
551 // A file/line/column triplet representing the macro definition location.
552 PPItemKey DefinitionLocation;
553 // A place to save the macro definition line string.
554 StringHandle DefinitionSourceLine;
555 // The header inclusion path handles for all the instances.
556 std::vector<InclusionPathHandle> InclusionPathHandles;
557};
558
559// Macro expansion instance tracker.
560//
561// This class represents one macro expansion, keyed by a PPItemKey.
562// It stores a string representing the macro reference in the source,
563// and a list of ConditionalExpansionInstances objects representing
564// the unique values the condition expands to in instances of the header.
565class MacroExpansionTracker {
566public:
567 MacroExpansionTracker(StringHandle MacroUnexpanded,
568 StringHandle MacroExpanded,
569 StringHandle InstanceSourceLine,
570 PPItemKey &DefinitionLocation,
571 StringHandle DefinitionSourceLine,
572 InclusionPathHandle InclusionPathHandle)
573 : MacroUnexpanded(MacroUnexpanded),
574 InstanceSourceLine(InstanceSourceLine) {
575 addMacroExpansionInstance(MacroExpanded, DefinitionLocation,
576 DefinitionSourceLine, InclusionPathHandle);
577 }
578 MacroExpansionTracker() {}
579
580 // Find a matching macro expansion instance.
581 MacroExpansionInstance *
582 findMacroExpansionInstance(StringHandle MacroExpanded,
583 PPItemKey &DefinitionLocation) {
584 for (auto I = MacroExpansionInstances.begin(),
585 E = MacroExpansionInstances.end();
586 I != E; ++I) {
587 if ((I->MacroExpanded == MacroExpanded) &&
588 (I->DefinitionLocation == DefinitionLocation)) {
589 return &*I; // Found.
590 }
591 }
592 return nullptr; // Not found.
593 }
594
595 // Add a macro expansion instance.
596 void addMacroExpansionInstance(StringHandle MacroExpanded,
597 PPItemKey &DefinitionLocation,
598 StringHandle DefinitionSourceLine,
599 InclusionPathHandle InclusionPathHandle) {
600 MacroExpansionInstances.push_back(
601 x: MacroExpansionInstance(MacroExpanded, DefinitionLocation,
602 DefinitionSourceLine, InclusionPathHandle));
603 }
604
605 // Return true if there is a mismatch.
606 bool hasMismatch() { return MacroExpansionInstances.size() > 1; }
607
608 // A string representing the macro instance without expansion.
609 StringHandle MacroUnexpanded;
610 // A place to save the macro instance source line string.
611 StringHandle InstanceSourceLine;
612 // The macro expansion instances.
613 // If all instances of the macro expansion expand to the same value,
614 // This vector will only have one instance.
615 std::vector<MacroExpansionInstance> MacroExpansionInstances;
616};
617
618// Conditional expansion instance.
619//
620// This class represents an instance of a condition exoression result
621// with a unique value. It also stores the unique header inclusion paths
622// for use in telling the user the nested include path to the header.
623class ConditionalExpansionInstance {
624public:
625 ConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue, InclusionPathHandle H)
626 : ConditionValue(ConditionValue) {
627 InclusionPathHandles.push_back(x: H);
628 }
629 ConditionalExpansionInstance() {}
630
631 // Check for the presence of a header inclusion path handle entry.
632 // Return false if not found.
633 bool haveInclusionPathHandle(InclusionPathHandle H) {
634 for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
635 I != E; ++I) {
636 if (*I == H)
637 return true;
638 }
639 return InclusionPathHandleInvalid;
640 }
641 // Add a new header inclusion path entry, if not already present.
642 void addInclusionPathHandle(InclusionPathHandle H) {
643 if (!haveInclusionPathHandle(H))
644 InclusionPathHandles.push_back(x: H);
645 }
646
647 // A flag representing the evaluated condition value.
648 clang::PPCallbacks::ConditionValueKind ConditionValue;
649 // The header inclusion path handles for all the instances.
650 std::vector<InclusionPathHandle> InclusionPathHandles;
651};
652
653// Conditional directive instance tracker.
654//
655// This class represents one conditional directive, keyed by a PPItemKey.
656// It stores a string representing the macro reference in the source,
657// and a list of ConditionExpansionInstance objects representing
658// the unique value the condition expression expands to in instances of
659// the header.
660class ConditionalTracker {
661public:
662 ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind,
663 clang::PPCallbacks::ConditionValueKind ConditionValue,
664 StringHandle ConditionUnexpanded,
665 InclusionPathHandle InclusionPathHandle)
666 : DirectiveKind(DirectiveKind), ConditionUnexpanded(ConditionUnexpanded) {
667 addConditionalExpansionInstance(ConditionValue, InclusionPathHandle);
668 }
669 ConditionalTracker() {}
670
671 // Find a matching condition expansion instance.
672 ConditionalExpansionInstance *
673 findConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue) {
674 for (auto I = ConditionalExpansionInstances.begin(),
675 E = ConditionalExpansionInstances.end();
676 I != E; ++I) {
677 if (I->ConditionValue == ConditionValue) {
678 return &*I; // Found.
679 }
680 }
681 return nullptr; // Not found.
682 }
683
684 // Add a conditional expansion instance.
685 void
686 addConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue,
687 InclusionPathHandle InclusionPathHandle) {
688 ConditionalExpansionInstances.push_back(
689 x: ConditionalExpansionInstance(ConditionValue, InclusionPathHandle));
690 }
691
692 // Return true if there is a mismatch.
693 bool hasMismatch() { return ConditionalExpansionInstances.size() > 1; }
694
695 // The kind of directive.
696 clang::tok::PPKeywordKind DirectiveKind;
697 // A string representing the macro instance without expansion.
698 StringHandle ConditionUnexpanded;
699 // The condition expansion instances.
700 // If all instances of the conditional expression expand to the same value,
701 // This vector will only have one instance.
702 std::vector<ConditionalExpansionInstance> ConditionalExpansionInstances;
703};
704
705class PreprocessorTrackerImpl;
706
707// Preprocessor callbacks for modularize.
708//
709// This class derives from the Clang PPCallbacks class to track preprocessor
710// actions, such as changing files and handling preprocessor directives and
711// macro expansions. It has to figure out when a new header file is entered
712// and left, as the provided handler is not particularly clear about it.
713class PreprocessorCallbacks : public clang::PPCallbacks {
714public:
715 PreprocessorCallbacks(PreprocessorTrackerImpl &ppTracker,
716 clang::Preprocessor &PP, llvm::StringRef rootHeaderFile)
717 : PPTracker(ppTracker), PP(PP), RootHeaderFile(rootHeaderFile) {}
718 ~PreprocessorCallbacks() override {}
719
720 // Overridden handlers.
721 void
722 InclusionDirective(clang::SourceLocation HashLoc,
723 const clang::Token &IncludeTok, llvm::StringRef FileName,
724 bool IsAngled, clang::CharSourceRange FilenameRange,
725 clang::OptionalFileEntryRef File,
726 llvm::StringRef SearchPath, llvm::StringRef RelativePath,
727 const clang::Module *SuggestedModule, bool ModuleImported,
728 clang::SrcMgr::CharacteristicKind FileType) override;
729 void FileChanged(clang::SourceLocation Loc,
730 clang::PPCallbacks::FileChangeReason Reason,
731 clang::SrcMgr::CharacteristicKind FileType,
732 clang::FileID PrevFID = clang::FileID()) override;
733 void MacroExpands(const clang::Token &MacroNameTok,
734 const clang::MacroDefinition &MD, clang::SourceRange Range,
735 const clang::MacroArgs *Args) override;
736 void Defined(const clang::Token &MacroNameTok,
737 const clang::MacroDefinition &MD,
738 clang::SourceRange Range) override;
739 void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
740 clang::PPCallbacks::ConditionValueKind ConditionResult) override;
741 void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
742 clang::PPCallbacks::ConditionValueKind ConditionResult,
743 clang::SourceLocation IfLoc) override;
744 void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
745 const clang::MacroDefinition &MD) override;
746 void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
747 const clang::MacroDefinition &MD) override;
748
749private:
750 PreprocessorTrackerImpl &PPTracker;
751 clang::Preprocessor &PP;
752 std::string RootHeaderFile;
753};
754
755// Preprocessor macro expansion item map types.
756typedef std::map<PPItemKey, MacroExpansionTracker> MacroExpansionMap;
757typedef std::map<PPItemKey, MacroExpansionTracker>::iterator
758MacroExpansionMapIter;
759
760// Preprocessor conditional expansion item map types.
761typedef std::map<PPItemKey, ConditionalTracker> ConditionalExpansionMap;
762typedef std::map<PPItemKey, ConditionalTracker>::iterator
763ConditionalExpansionMapIter;
764
765// Preprocessor tracker for modularize.
766//
767// This class stores information about all the headers processed in the
768// course of running modularize.
769class PreprocessorTrackerImpl : public PreprocessorTracker {
770public:
771 PreprocessorTrackerImpl(llvm::SmallVector<std::string, 32> &Headers,
772 bool DoBlockCheckHeaderListOnly)
773 : BlockCheckHeaderListOnly(DoBlockCheckHeaderListOnly),
774 CurrentInclusionPathHandle(InclusionPathHandleInvalid),
775 InNestedHeader(false) {
776 // Use canonical header path representation.
777 for (llvm::ArrayRef<std::string>::iterator I = Headers.begin(),
778 E = Headers.end();
779 I != E; ++I) {
780 HeaderList.push_back(Elt: getCanonicalPath(path: *I));
781 }
782 }
783
784 ~PreprocessorTrackerImpl() override {}
785
786 // Handle entering a preprocessing session.
787 void handlePreprocessorEntry(clang::Preprocessor &PP,
788 llvm::StringRef rootHeaderFile) override {
789 HeadersInThisCompile.clear();
790 assert((HeaderStack.size() == 0) && "Header stack should be empty.");
791 pushHeaderHandle(H: addHeader(HeaderPath: rootHeaderFile));
792 PP.addPPCallbacks(C: std::make_unique<PreprocessorCallbacks>(args&: *this, args&: PP,
793 args&: rootHeaderFile));
794 }
795 // Handle exiting a preprocessing session.
796 void handlePreprocessorExit() override { HeaderStack.clear(); }
797
798 // Handle include directive.
799 // This function is called every time an include directive is seen by the
800 // preprocessor, for the purpose of later checking for 'extern "" {}' or
801 // "namespace {}" blocks containing #include directives.
802 void handleIncludeDirective(llvm::StringRef DirectivePath, int DirectiveLine,
803 int DirectiveColumn,
804 llvm::StringRef TargetPath) override {
805 // If it's not a header in the header list, ignore it with respect to
806 // the check.
807 if (BlockCheckHeaderListOnly && !isHeaderListHeader(HeaderPath: TargetPath))
808 return;
809 HeaderHandle CurrentHeaderHandle = findHeaderHandle(HeaderPath: DirectivePath);
810 StringHandle IncludeHeaderHandle = addString(Str: TargetPath);
811 for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),
812 E = IncludeDirectives.end();
813 I != E; ++I) {
814 // If we already have an entry for this directive, return now.
815 if ((I->File == CurrentHeaderHandle) && (I->Line == DirectiveLine))
816 return;
817 }
818 PPItemKey IncludeDirectiveItem(IncludeHeaderHandle, CurrentHeaderHandle,
819 DirectiveLine, DirectiveColumn);
820 IncludeDirectives.push_back(x: IncludeDirectiveItem);
821 }
822
823 // Check for include directives within the given source line range.
824 // Report errors if any found. Returns true if no include directives
825 // found in block.
826 bool checkForIncludesInBlock(clang::Preprocessor &PP,
827 clang::SourceRange BlockSourceRange,
828 const char *BlockIdentifierMessage,
829 llvm::raw_ostream &OS) override {
830 clang::SourceLocation BlockStartLoc = BlockSourceRange.getBegin();
831 clang::SourceLocation BlockEndLoc = BlockSourceRange.getEnd();
832 // Use block location to get FileID of both the include directive
833 // and block statement.
834 clang::FileID FileID = PP.getSourceManager().getFileID(SpellingLoc: BlockStartLoc);
835 std::string SourcePath = getSourceLocationFile(PP, Loc: BlockStartLoc);
836 SourcePath = ModularizeUtilities::getCanonicalPath(FilePath: SourcePath);
837 HeaderHandle SourceHandle = findHeaderHandle(HeaderPath: SourcePath);
838 if (SourceHandle == -1)
839 return true;
840 int BlockStartLine, BlockStartColumn, BlockEndLine, BlockEndColumn;
841 bool returnValue = true;
842 getSourceLocationLineAndColumn(PP, Loc: BlockStartLoc, Line&: BlockStartLine,
843 Column&: BlockStartColumn);
844 getSourceLocationLineAndColumn(PP, Loc: BlockEndLoc, Line&: BlockEndLine,
845 Column&: BlockEndColumn);
846 for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),
847 E = IncludeDirectives.end();
848 I != E; ++I) {
849 // If we find an entry within the block, report an error.
850 if ((I->File == SourceHandle) && (I->Line >= BlockStartLine) &&
851 (I->Line < BlockEndLine)) {
852 returnValue = false;
853 OS << SourcePath << ":" << I->Line << ":" << I->Column << ":\n";
854 OS << getSourceLine(PP, FileID, Line: I->Line) << "\n";
855 if (I->Column > 0)
856 OS << std::string(I->Column - 1, ' ') << "^\n";
857 OS << "error: Include directive within " << BlockIdentifierMessage
858 << ".\n";
859 OS << SourcePath << ":" << BlockStartLine << ":" << BlockStartColumn
860 << ":\n";
861 OS << getSourceLine(PP, Loc: BlockStartLoc) << "\n";
862 if (BlockStartColumn > 0)
863 OS << std::string(BlockStartColumn - 1, ' ') << "^\n";
864 OS << "The \"" << BlockIdentifierMessage << "\" block is here.\n";
865 }
866 }
867 return returnValue;
868 }
869
870 // Handle entering a header source file.
871 void handleHeaderEntry(clang::Preprocessor &PP, llvm::StringRef HeaderPath) {
872 // Ignore <built-in> and <command-line> to reduce message clutter.
873 if (HeaderPath.starts_with(Prefix: "<"))
874 return;
875 HeaderHandle H = addHeader(HeaderPath);
876 if (H != getCurrentHeaderHandle())
877 pushHeaderHandle(H);
878 // Check for nested header.
879 if (!InNestedHeader)
880 InNestedHeader = !HeadersInThisCompile.insert(V: H).second;
881 }
882
883 // Handle exiting a header source file.
884 void handleHeaderExit(llvm::StringRef HeaderPath) {
885 // Ignore <built-in> and <command-line> to reduce message clutter.
886 if (HeaderPath.starts_with(Prefix: "<"))
887 return;
888 HeaderHandle H = findHeaderHandle(HeaderPath);
889 HeaderHandle TH;
890 if (isHeaderHandleInStack(H)) {
891 do {
892 TH = getCurrentHeaderHandle();
893 popHeaderHandle();
894 } while ((TH != H) && (HeaderStack.size() != 0));
895 }
896 InNestedHeader = false;
897 }
898
899 // Lookup/add string.
900 StringHandle addString(llvm::StringRef Str) {
901 return Strings.insert(key: Str).first->first();
902 }
903
904 // Convert to a canonical path.
905 std::string getCanonicalPath(llvm::StringRef path) const {
906 std::string CanonicalPath(path);
907 llvm::replace(Range&: CanonicalPath, OldValue: '\\', NewValue: '/');
908 return CanonicalPath;
909 }
910
911 // Return true if the given header is in the header list.
912 bool isHeaderListHeader(llvm::StringRef HeaderPath) const {
913 std::string CanonicalPath = getCanonicalPath(path: HeaderPath);
914 for (llvm::ArrayRef<std::string>::iterator I = HeaderList.begin(),
915 E = HeaderList.end();
916 I != E; ++I) {
917 if (*I == CanonicalPath)
918 return true;
919 }
920 return false;
921 }
922
923 // Get the handle of a header file entry.
924 // Return HeaderHandleInvalid if not found.
925 HeaderHandle findHeaderHandle(llvm::StringRef HeaderPath) const {
926 std::string CanonicalPath = getCanonicalPath(path: HeaderPath);
927 HeaderHandle H = 0;
928 for (auto I = HeaderPaths.begin(), E = HeaderPaths.end(); I != E;
929 ++I, ++H) {
930 if (*I == CanonicalPath)
931 return H;
932 }
933 return HeaderHandleInvalid;
934 }
935
936 // Add a new header file entry, or return existing handle.
937 // Return the header handle.
938 HeaderHandle addHeader(llvm::StringRef HeaderPath) {
939 std::string CanonicalPath = getCanonicalPath(path: HeaderPath);
940 HeaderHandle H = findHeaderHandle(HeaderPath: CanonicalPath);
941 if (H == HeaderHandleInvalid) {
942 H = HeaderPaths.size();
943 HeaderPaths.push_back(x: addString(Str: CanonicalPath));
944 }
945 return H;
946 }
947
948 // Return a header file path string given its handle.
949 StringHandle getHeaderFilePath(HeaderHandle H) const {
950 if ((H >= 0) && (H < (HeaderHandle)HeaderPaths.size()))
951 return HeaderPaths[H];
952 return StringHandle();
953 }
954
955 // Returns a handle to the inclusion path.
956 InclusionPathHandle pushHeaderHandle(HeaderHandle H) {
957 HeaderStack.push_back(x: H);
958 return CurrentInclusionPathHandle = addInclusionPathHandle(Path: HeaderStack);
959 }
960 // Pops the last header handle from the stack;
961 void popHeaderHandle() {
962 // assert((HeaderStack.size() != 0) && "Header stack already empty.");
963 if (HeaderStack.size() != 0) {
964 HeaderStack.pop_back();
965 CurrentInclusionPathHandle = addInclusionPathHandle(Path: HeaderStack);
966 }
967 }
968 // Get the top handle on the header stack.
969 HeaderHandle getCurrentHeaderHandle() const {
970 if (HeaderStack.size() != 0)
971 return HeaderStack.back();
972 return HeaderHandleInvalid;
973 }
974
975 // Check for presence of header handle in the header stack.
976 bool isHeaderHandleInStack(HeaderHandle H) const {
977 return llvm::is_contained(Range: HeaderStack, Element: H);
978 }
979
980 // Get the handle of a header inclusion path entry.
981 // Return InclusionPathHandleInvalid if not found.
982 InclusionPathHandle
983 findInclusionPathHandle(const std::vector<HeaderHandle> &Path) const {
984 InclusionPathHandle H = 0;
985 for (auto I = InclusionPaths.begin(), E = InclusionPaths.end(); I != E;
986 ++I, ++H) {
987 if (I->Path == Path)
988 return H;
989 }
990 return HeaderHandleInvalid;
991 }
992 // Add a new header inclusion path entry, or return existing handle.
993 // Return the header inclusion path entry handle.
994 InclusionPathHandle
995 addInclusionPathHandle(const std::vector<HeaderHandle> &Path) {
996 InclusionPathHandle H = findInclusionPathHandle(Path);
997 if (H == HeaderHandleInvalid) {
998 H = InclusionPaths.size();
999 InclusionPaths.push_back(x: HeaderInclusionPath(Path));
1000 }
1001 return H;
1002 }
1003 // Return the current inclusion path handle.
1004 InclusionPathHandle getCurrentInclusionPathHandle() const {
1005 return CurrentInclusionPathHandle;
1006 }
1007
1008 // Return an inclusion path given its handle.
1009 const std::vector<HeaderHandle> &
1010 getInclusionPath(InclusionPathHandle H) const {
1011 if ((H >= 0) && (H <= (InclusionPathHandle)InclusionPaths.size()))
1012 return InclusionPaths[H].Path;
1013 static std::vector<HeaderHandle> Empty;
1014 return Empty;
1015 }
1016
1017 // Add a macro expansion instance.
1018 void addMacroExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
1019 clang::SourceLocation InstanceLoc,
1020 clang::SourceLocation DefinitionLoc,
1021 clang::IdentifierInfo *II,
1022 llvm::StringRef MacroUnexpanded,
1023 llvm::StringRef MacroExpanded,
1024 InclusionPathHandle InclusionPathHandle) {
1025 if (InNestedHeader)
1026 return;
1027 StringHandle MacroName = addString(Str: II->getName());
1028 PPItemKey InstanceKey(PP, MacroName, H, InstanceLoc);
1029 PPItemKey DefinitionKey(PP, MacroName, H, DefinitionLoc);
1030 auto I = MacroExpansions.find(x: InstanceKey);
1031 // If existing instance of expansion not found, add one.
1032 if (I == MacroExpansions.end()) {
1033 std::string InstanceSourceLine =
1034 getSourceLocationString(PP, Loc: InstanceLoc) + ":\n" +
1035 getSourceLine(PP, Loc: InstanceLoc) + "\n";
1036 std::string DefinitionSourceLine =
1037 getSourceLocationString(PP, Loc: DefinitionLoc) + ":\n" +
1038 getSourceLine(PP, Loc: DefinitionLoc) + "\n";
1039 MacroExpansions[InstanceKey] = MacroExpansionTracker(
1040 addString(Str: MacroUnexpanded), addString(Str: MacroExpanded),
1041 addString(Str: InstanceSourceLine), DefinitionKey,
1042 addString(Str: DefinitionSourceLine), InclusionPathHandle);
1043 } else {
1044 // We've seen the macro before. Get its tracker.
1045 MacroExpansionTracker &CondTracker = I->second;
1046 // Look up an existing instance value for the macro.
1047 MacroExpansionInstance *MacroInfo =
1048 CondTracker.findMacroExpansionInstance(MacroExpanded: addString(Str: MacroExpanded),
1049 DefinitionLocation&: DefinitionKey);
1050 // If found, just add the inclusion path to the instance.
1051 if (MacroInfo)
1052 MacroInfo->addInclusionPathHandle(H: InclusionPathHandle);
1053 else {
1054 // Otherwise add a new instance with the unique value.
1055 std::string DefinitionSourceLine =
1056 getSourceLocationString(PP, Loc: DefinitionLoc) + ":\n" +
1057 getSourceLine(PP, Loc: DefinitionLoc) + "\n";
1058 CondTracker.addMacroExpansionInstance(
1059 MacroExpanded: addString(Str: MacroExpanded), DefinitionLocation&: DefinitionKey,
1060 DefinitionSourceLine: addString(Str: DefinitionSourceLine), InclusionPathHandle);
1061 }
1062 }
1063 }
1064
1065 // Add a conditional expansion instance.
1066 void
1067 addConditionalExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
1068 clang::SourceLocation InstanceLoc,
1069 clang::tok::PPKeywordKind DirectiveKind,
1070 clang::PPCallbacks::ConditionValueKind ConditionValue,
1071 llvm::StringRef ConditionUnexpanded,
1072 InclusionPathHandle InclusionPathHandle) {
1073 // Ignore header guards, assuming the header guard is the only conditional.
1074 if (InNestedHeader)
1075 return;
1076 StringHandle ConditionUnexpandedHandle(addString(Str: ConditionUnexpanded));
1077 PPItemKey InstanceKey(PP, ConditionUnexpandedHandle, H, InstanceLoc);
1078 auto I = ConditionalExpansions.find(x: InstanceKey);
1079 // If existing instance of condition not found, add one.
1080 if (I == ConditionalExpansions.end()) {
1081 std::string InstanceSourceLine =
1082 getSourceLocationString(PP, Loc: InstanceLoc) + ":\n" +
1083 getSourceLine(PP, Loc: InstanceLoc) + "\n";
1084 ConditionalExpansions[InstanceKey] =
1085 ConditionalTracker(DirectiveKind, ConditionValue,
1086 ConditionUnexpandedHandle, InclusionPathHandle);
1087 } else {
1088 // We've seen the conditional before. Get its tracker.
1089 ConditionalTracker &CondTracker = I->second;
1090 // Look up an existing instance value for the condition.
1091 ConditionalExpansionInstance *MacroInfo =
1092 CondTracker.findConditionalExpansionInstance(ConditionValue);
1093 // If found, just add the inclusion path to the instance.
1094 if (MacroInfo)
1095 MacroInfo->addInclusionPathHandle(H: InclusionPathHandle);
1096 else {
1097 // Otherwise add a new instance with the unique value.
1098 CondTracker.addConditionalExpansionInstance(ConditionValue,
1099 InclusionPathHandle);
1100 }
1101 }
1102 }
1103
1104 // Report on inconsistent macro instances.
1105 // Returns true if any mismatches.
1106 bool reportInconsistentMacros(llvm::raw_ostream &OS) override {
1107 bool ReturnValue = false;
1108 // Walk all the macro expansion trackers in the map.
1109 for (auto I = MacroExpansions.begin(), E = MacroExpansions.end(); I != E;
1110 ++I) {
1111 const PPItemKey &ItemKey = I->first;
1112 MacroExpansionTracker &MacroExpTracker = I->second;
1113 // If no mismatch (only one instance value) continue.
1114 if (!MacroExpTracker.hasMismatch())
1115 continue;
1116 // Tell caller we found one or more errors.
1117 ReturnValue = true;
1118 // Start the error message.
1119 OS << MacroExpTracker.InstanceSourceLine;
1120 if (ItemKey.Column > 0)
1121 OS << std::string(ItemKey.Column - 1, ' ') << "^\n";
1122 OS << "error: Macro instance '" << MacroExpTracker.MacroUnexpanded
1123 << "' has different values in this header, depending on how it was "
1124 "included.\n";
1125 // Walk all the instances.
1126 for (auto IMT = MacroExpTracker.MacroExpansionInstances.begin(),
1127 EMT = MacroExpTracker.MacroExpansionInstances.end();
1128 IMT != EMT; ++IMT) {
1129 MacroExpansionInstance &MacroInfo = *IMT;
1130 OS << " '" << MacroExpTracker.MacroUnexpanded << "' expanded to: '"
1131 << MacroInfo.MacroExpanded
1132 << "' with respect to these inclusion paths:\n";
1133 // Walk all the inclusion path hierarchies.
1134 for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
1135 EIP = MacroInfo.InclusionPathHandles.end();
1136 IIP != EIP; ++IIP) {
1137 const std::vector<HeaderHandle> &ip = getInclusionPath(H: *IIP);
1138 auto Count = (int)ip.size();
1139 for (int Index = 0; Index < Count; ++Index) {
1140 HeaderHandle H = ip[Index];
1141 OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H)
1142 << "\n";
1143 }
1144 }
1145 // For a macro that wasn't defined, we flag it by using the
1146 // instance location.
1147 // If there is a definition...
1148 if (MacroInfo.DefinitionLocation.Line != ItemKey.Line) {
1149 OS << MacroInfo.DefinitionSourceLine;
1150 if (MacroInfo.DefinitionLocation.Column > 0)
1151 OS << std::string(MacroInfo.DefinitionLocation.Column - 1, ' ')
1152 << "^\n";
1153 OS << "Macro defined here.\n";
1154 } else
1155 OS << "(no macro definition)"
1156 << "\n";
1157 }
1158 }
1159 return ReturnValue;
1160 }
1161
1162 // Report on inconsistent conditional instances.
1163 // Returns true if any mismatches.
1164 bool reportInconsistentConditionals(llvm::raw_ostream &OS) override {
1165 bool ReturnValue = false;
1166 // Walk all the conditional trackers in the map.
1167 for (auto I = ConditionalExpansions.begin(),
1168 E = ConditionalExpansions.end();
1169 I != E; ++I) {
1170 const PPItemKey &ItemKey = I->first;
1171 ConditionalTracker &CondTracker = I->second;
1172 if (!CondTracker.hasMismatch())
1173 continue;
1174 // Tell caller we found one or more errors.
1175 ReturnValue = true;
1176 // Start the error message.
1177 OS << HeaderPaths[ItemKey.File] << ":" << ItemKey.Line << ":"
1178 << ItemKey.Column << "\n";
1179 OS << "#" << getDirectiveSpelling(kind: CondTracker.DirectiveKind) << " "
1180 << CondTracker.ConditionUnexpanded << "\n";
1181 OS << "^\n";
1182 OS << "error: Conditional expression instance '"
1183 << CondTracker.ConditionUnexpanded
1184 << "' has different values in this header, depending on how it was "
1185 "included.\n";
1186 // Walk all the instances.
1187 for (auto IMT = CondTracker.ConditionalExpansionInstances.begin(),
1188 EMT = CondTracker.ConditionalExpansionInstances.end();
1189 IMT != EMT; ++IMT) {
1190 ConditionalExpansionInstance &MacroInfo = *IMT;
1191 OS << " '" << CondTracker.ConditionUnexpanded << "' expanded to: '"
1192 << ConditionValueKindStrings[MacroInfo.ConditionValue]
1193 << "' with respect to these inclusion paths:\n";
1194 // Walk all the inclusion path hierarchies.
1195 for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
1196 EIP = MacroInfo.InclusionPathHandles.end();
1197 IIP != EIP; ++IIP) {
1198 const std::vector<HeaderHandle> &ip = getInclusionPath(H: *IIP);
1199 auto Count = (int)ip.size();
1200 for (int Index = 0; Index < Count; ++Index) {
1201 HeaderHandle H = ip[Index];
1202 OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H)
1203 << "\n";
1204 }
1205 }
1206 }
1207 }
1208 return ReturnValue;
1209 }
1210
1211 // Get directive spelling.
1212 static const char *getDirectiveSpelling(clang::tok::PPKeywordKind kind) {
1213 switch (kind) {
1214 case clang::tok::pp_if:
1215 return "if";
1216 case clang::tok::pp_elif:
1217 return "elif";
1218 case clang::tok::pp_ifdef:
1219 return "ifdef";
1220 case clang::tok::pp_ifndef:
1221 return "ifndef";
1222 default:
1223 return "(unknown)";
1224 }
1225 }
1226
1227private:
1228 llvm::SmallVector<std::string, 32> HeaderList;
1229 // Only do extern, namespace check for headers in HeaderList.
1230 bool BlockCheckHeaderListOnly;
1231 llvm::StringSet<> Strings;
1232 std::vector<StringHandle> HeaderPaths;
1233 std::vector<HeaderHandle> HeaderStack;
1234 std::vector<HeaderInclusionPath> InclusionPaths;
1235 InclusionPathHandle CurrentInclusionPathHandle;
1236 llvm::SmallSet<HeaderHandle, 32> HeadersInThisCompile;
1237 std::vector<PPItemKey> IncludeDirectives;
1238 MacroExpansionMap MacroExpansions;
1239 ConditionalExpansionMap ConditionalExpansions;
1240 bool InNestedHeader;
1241};
1242
1243} // namespace
1244
1245// PreprocessorTracker functions.
1246
1247// PreprocessorTracker destructor.
1248PreprocessorTracker::~PreprocessorTracker() {}
1249
1250// Create instance of PreprocessorTracker.
1251PreprocessorTracker *PreprocessorTracker::create(
1252 llvm::SmallVector<std::string, 32> &Headers,
1253 bool DoBlockCheckHeaderListOnly) {
1254 return new PreprocessorTrackerImpl(Headers, DoBlockCheckHeaderListOnly);
1255}
1256
1257// Preprocessor callbacks for modularize.
1258
1259// Handle include directive.
1260void PreprocessorCallbacks::InclusionDirective(
1261 clang::SourceLocation HashLoc, const clang::Token &IncludeTok,
1262 llvm::StringRef FileName, bool IsAngled,
1263 clang::CharSourceRange FilenameRange, clang::OptionalFileEntryRef File,
1264 llvm::StringRef SearchPath, llvm::StringRef RelativePath,
1265 const clang::Module *SuggestedModule, bool ModuleImported,
1266 clang::SrcMgr::CharacteristicKind FileType) {
1267 int DirectiveLine, DirectiveColumn;
1268 std::string HeaderPath = getSourceLocationFile(PP, Loc: HashLoc);
1269 getSourceLocationLineAndColumn(PP, Loc: HashLoc, Line&: DirectiveLine, Column&: DirectiveColumn);
1270 PPTracker.handleIncludeDirective(DirectivePath: HeaderPath, DirectiveLine, DirectiveColumn,
1271 TargetPath: FileName);
1272}
1273
1274// Handle file entry/exit.
1275void PreprocessorCallbacks::FileChanged(
1276 clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason,
1277 clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) {
1278 switch (Reason) {
1279 case EnterFile:
1280 PPTracker.handleHeaderEntry(PP, HeaderPath: getSourceLocationFile(PP, Loc));
1281 break;
1282 case ExitFile: {
1283 clang::OptionalFileEntryRef F =
1284 PP.getSourceManager().getFileEntryRefForID(FID: PrevFID);
1285 if (F)
1286 PPTracker.handleHeaderExit(HeaderPath: F->getName());
1287 } break;
1288 case SystemHeaderPragma:
1289 case RenameFile:
1290 break;
1291 }
1292}
1293
1294// Handle macro expansion.
1295void PreprocessorCallbacks::MacroExpands(const clang::Token &MacroNameTok,
1296 const clang::MacroDefinition &MD,
1297 clang::SourceRange Range,
1298 const clang::MacroArgs *Args) {
1299 clang::SourceLocation Loc = Range.getBegin();
1300 // Ignore macro argument expansions.
1301 if (!Loc.isFileID())
1302 return;
1303 clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
1304 const clang::MacroInfo *MI = MD.getMacroInfo();
1305 std::string MacroName = II->getName().str();
1306 std::string Unexpanded(getMacroUnexpandedString(Range, PP, MacroName, MI));
1307 std::string Expanded(getMacroExpandedString(PP, MacroName, MI, Args));
1308 PPTracker.addMacroExpansionInstance(
1309 PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc, DefinitionLoc: MI->getDefinitionLoc(), II,
1310 MacroUnexpanded: Unexpanded, MacroExpanded: Expanded, InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle());
1311}
1312
1313void PreprocessorCallbacks::Defined(const clang::Token &MacroNameTok,
1314 const clang::MacroDefinition &MD,
1315 clang::SourceRange Range) {
1316 clang::SourceLocation Loc(Range.getBegin());
1317 clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
1318 const clang::MacroInfo *MI = MD.getMacroInfo();
1319 std::string Unexpanded(getSourceString(PP, Range));
1320 PPTracker.addMacroExpansionInstance(
1321 PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc,
1322 DefinitionLoc: (MI ? MI->getDefinitionLoc() : Loc), II, MacroUnexpanded: Unexpanded,
1323 MacroExpanded: (MI ? "true" : "false"), InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle());
1324}
1325
1326void PreprocessorCallbacks::If(clang::SourceLocation Loc,
1327 clang::SourceRange ConditionRange,
1328 clang::PPCallbacks::ConditionValueKind ConditionResult) {
1329 std::string Unexpanded(getSourceString(PP, Range: ConditionRange));
1330 PPTracker.addConditionalExpansionInstance(
1331 PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc, DirectiveKind: clang::tok::pp_if,
1332 ConditionValue: ConditionResult, ConditionUnexpanded: Unexpanded, InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle());
1333}
1334
1335void PreprocessorCallbacks::Elif(clang::SourceLocation Loc,
1336 clang::SourceRange ConditionRange,
1337 clang::PPCallbacks::ConditionValueKind ConditionResult,
1338 clang::SourceLocation IfLoc) {
1339 std::string Unexpanded(getSourceString(PP, Range: ConditionRange));
1340 PPTracker.addConditionalExpansionInstance(
1341 PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc, DirectiveKind: clang::tok::pp_elif,
1342 ConditionValue: ConditionResult, ConditionUnexpanded: Unexpanded, InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle());
1343}
1344
1345void PreprocessorCallbacks::Ifdef(clang::SourceLocation Loc,
1346 const clang::Token &MacroNameTok,
1347 const clang::MacroDefinition &MD) {
1348 clang::PPCallbacks::ConditionValueKind IsDefined =
1349 (MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
1350 PPTracker.addConditionalExpansionInstance(
1351 PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc, DirectiveKind: clang::tok::pp_ifdef,
1352 ConditionValue: IsDefined, ConditionUnexpanded: PP.getSpelling(Tok: MacroNameTok),
1353 InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle());
1354}
1355
1356void PreprocessorCallbacks::Ifndef(clang::SourceLocation Loc,
1357 const clang::Token &MacroNameTok,
1358 const clang::MacroDefinition &MD) {
1359 clang::PPCallbacks::ConditionValueKind IsNotDefined =
1360 (!MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
1361 PPTracker.addConditionalExpansionInstance(
1362 PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc, DirectiveKind: clang::tok::pp_ifndef,
1363 ConditionValue: IsNotDefined, ConditionUnexpanded: PP.getSpelling(Tok: MacroNameTok),
1364 InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle());
1365}
1366} // end namespace Modularize
1367

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang-tools-extra/modularize/PreprocessorTracker.cpp