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. |
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(SM: PP.getSourceManager()); |
275 | } |
276 | |
277 | // Get just the file name from a source location. |
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(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. |
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. |
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(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. |
313 | static 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. |
339 | static 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. |
377 | static std::string getMacroUnexpandedString(clang::SourceRange Range, |
378 | clang::Preprocessor &PP, |
379 | llvm::StringRef MacroName, |
380 | const clang::MacroInfo *MI) { |
381 | clang::SourceLocation BeginLoc(Range.getBegin()); |
382 | const char *BeginPtr = PP.getSourceManager().getCharacterData(SL: BeginLoc); |
383 | size_t Length; |
384 | std::string Unexpanded; |
385 | if (MI->isFunctionLike()) { |
386 | clang::SourceLocation EndLoc(Range.getEnd()); |
387 | const char *EndPtr = PP.getSourceManager().getCharacterData(SL: EndLoc) + 1; |
388 | Length = (EndPtr - BeginPtr) + 1; // +1 is ')' width. |
389 | } else |
390 | Length = MacroName.size(); |
391 | return llvm::StringRef(BeginPtr, Length).trim().str(); |
392 | } |
393 | |
394 | // Get the expansion for a macro instance, given the information |
395 | // provided by PPCallbacks. |
396 | // FIXME: This doesn't support function-style macro instances |
397 | // passed as arguments to another function-style macro. However, |
398 | // since it still expands the inner arguments, it still |
399 | // allows modularize to effectively work with respect to macro |
400 | // consistency checking, although it displays the incorrect |
401 | // expansion in error messages. |
402 | static std::string getMacroExpandedString(clang::Preprocessor &PP, |
403 | llvm::StringRef MacroName, |
404 | const clang::MacroInfo *MI, |
405 | const clang::MacroArgs *Args) { |
406 | std::string Expanded; |
407 | // Walk over the macro Tokens. |
408 | for (const auto &T : MI->tokens()) { |
409 | clang::IdentifierInfo *II = T.getIdentifierInfo(); |
410 | int ArgNo = (II && Args ? MI->getParameterNum(Arg: II) : -1); |
411 | if (ArgNo == -1) { |
412 | // This isn't an argument, just add it. |
413 | if (II == nullptr) |
414 | Expanded += PP.getSpelling(Tok: T); // Not an identifier. |
415 | else { |
416 | // Token is for an identifier. |
417 | std::string Name = II->getName().str(); |
418 | // Check for nexted macro references. |
419 | clang::MacroInfo *MacroInfo = PP.getMacroInfo(II); |
420 | if (MacroInfo && (Name != MacroName)) |
421 | Expanded += getMacroExpandedString(PP, MacroName: Name, MI: MacroInfo, Args: nullptr); |
422 | else |
423 | Expanded += Name; |
424 | } |
425 | continue; |
426 | } |
427 | // We get here if it's a function-style macro with arguments. |
428 | const clang::Token *ResultArgToks; |
429 | const clang::Token *ArgTok = Args->getUnexpArgument(Arg: ArgNo); |
430 | if (Args->ArgNeedsPreexpansion(ArgTok, PP)) |
431 | ResultArgToks = &(const_cast<clang::MacroArgs *>(Args)) |
432 | ->getPreExpArgument(Arg: ArgNo, PP)[0]; |
433 | else |
434 | ResultArgToks = ArgTok; // Use non-preexpanded Tokens. |
435 | // If the arg token didn't expand into anything, ignore it. |
436 | if (ResultArgToks->is(K: clang::tok::eof)) |
437 | continue; |
438 | unsigned NumToks = clang::MacroArgs::getArgLength(ArgPtr: ResultArgToks); |
439 | // Append the resulting argument expansions. |
440 | for (unsigned ArgumentIndex = 0; ArgumentIndex < NumToks; ++ArgumentIndex) { |
441 | const clang::Token &AT = ResultArgToks[ArgumentIndex]; |
442 | clang::IdentifierInfo *II = AT.getIdentifierInfo(); |
443 | if (II == nullptr) |
444 | Expanded += PP.getSpelling(Tok: AT); // Not an identifier. |
445 | else { |
446 | // It's an identifier. Check for further expansion. |
447 | std::string Name = II->getName().str(); |
448 | clang::MacroInfo *MacroInfo = PP.getMacroInfo(II); |
449 | if (MacroInfo) |
450 | Expanded += getMacroExpandedString(PP, MacroName: Name, MI: MacroInfo, Args: nullptr); |
451 | else |
452 | Expanded += Name; |
453 | } |
454 | } |
455 | } |
456 | return Expanded; |
457 | } |
458 | |
459 | namespace { |
460 | |
461 | // ConditionValueKind strings. |
462 | const char * |
463 | ConditionValueKindStrings[] = { |
464 | "(not evaluated)" , "false" , "true" |
465 | }; |
466 | |
467 | // Preprocessor item key. |
468 | // |
469 | // This class represents a location in a source file, for use |
470 | // as a key representing a unique name/file/line/column quadruplet, |
471 | // which in this case is used to identify a macro expansion instance, |
472 | // but could be used for other things as well. |
473 | // The file is a header file handle, the line is a line number, |
474 | // and the column is a column number. |
475 | class PPItemKey { |
476 | public: |
477 | PPItemKey(clang::Preprocessor &PP, StringHandle Name, HeaderHandle File, |
478 | clang::SourceLocation Loc) |
479 | : Name(Name), File(File) { |
480 | getSourceLocationLineAndColumn(PP, Loc, Line, Column); |
481 | } |
482 | PPItemKey(StringHandle Name, HeaderHandle File, int Line, int Column) |
483 | : Name(Name), File(File), Line(Line), Column(Column) {} |
484 | PPItemKey(const PPItemKey &Other) |
485 | : Name(Other.Name), File(Other.File), Line(Other.Line), |
486 | Column(Other.Column) {} |
487 | PPItemKey() : File(HeaderHandleInvalid), Line(0), Column(0) {} |
488 | bool operator==(const PPItemKey &Other) const { |
489 | if (Name != Other.Name) |
490 | return false; |
491 | if (File != Other.File) |
492 | return false; |
493 | if (Line != Other.Line) |
494 | return false; |
495 | return Column == Other.Column; |
496 | } |
497 | bool operator<(const PPItemKey &Other) const { |
498 | if (Name < Other.Name) |
499 | return true; |
500 | else if (Name > Other.Name) |
501 | return false; |
502 | if (File < Other.File) |
503 | return true; |
504 | else if (File > Other.File) |
505 | return false; |
506 | if (Line < Other.Line) |
507 | return true; |
508 | else if (Line > Other.Line) |
509 | return false; |
510 | return Column < Other.Column; |
511 | } |
512 | StringHandle Name; |
513 | HeaderHandle File; |
514 | int Line; |
515 | int Column; |
516 | }; |
517 | |
518 | // Header inclusion path. |
519 | class { |
520 | public: |
521 | (std::vector<HeaderHandle> ) |
522 | : Path(HeaderInclusionPath) {} |
523 | (const HeaderInclusionPath &Other) : Path(Other.Path) {} |
524 | () {} |
525 | std::vector<HeaderHandle> ; |
526 | }; |
527 | |
528 | // Macro expansion instance. |
529 | // |
530 | // This class represents an instance of a macro expansion with a |
531 | // unique value. It also stores the unique header inclusion paths |
532 | // for use in telling the user the nested include path to the header. |
533 | class MacroExpansionInstance { |
534 | public: |
535 | MacroExpansionInstance(StringHandle MacroExpanded, |
536 | PPItemKey &DefinitionLocation, |
537 | StringHandle DefinitionSourceLine, |
538 | InclusionPathHandle H) |
539 | : MacroExpanded(MacroExpanded), DefinitionLocation(DefinitionLocation), |
540 | DefinitionSourceLine(DefinitionSourceLine) { |
541 | InclusionPathHandles.push_back(x: H); |
542 | } |
543 | MacroExpansionInstance() {} |
544 | |
545 | // Check for the presence of a header inclusion path handle entry. |
546 | // Return false if not found. |
547 | bool haveInclusionPathHandle(InclusionPathHandle H) { |
548 | for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end(); |
549 | I != E; ++I) { |
550 | if (*I == H) |
551 | return true; |
552 | } |
553 | return InclusionPathHandleInvalid; |
554 | } |
555 | // Add a new header inclusion path entry, if not already present. |
556 | void addInclusionPathHandle(InclusionPathHandle H) { |
557 | if (!haveInclusionPathHandle(H)) |
558 | InclusionPathHandles.push_back(x: H); |
559 | } |
560 | |
561 | // A string representing the macro instance after preprocessing. |
562 | StringHandle MacroExpanded; |
563 | // A file/line/column triplet representing the macro definition location. |
564 | PPItemKey DefinitionLocation; |
565 | // A place to save the macro definition line string. |
566 | StringHandle DefinitionSourceLine; |
567 | // The header inclusion path handles for all the instances. |
568 | std::vector<InclusionPathHandle> InclusionPathHandles; |
569 | }; |
570 | |
571 | // Macro expansion instance tracker. |
572 | // |
573 | // This class represents one macro expansion, keyed by a PPItemKey. |
574 | // It stores a string representing the macro reference in the source, |
575 | // and a list of ConditionalExpansionInstances objects representing |
576 | // the unique values the condition expands to in instances of the header. |
577 | class MacroExpansionTracker { |
578 | public: |
579 | MacroExpansionTracker(StringHandle MacroUnexpanded, |
580 | StringHandle MacroExpanded, |
581 | StringHandle InstanceSourceLine, |
582 | PPItemKey &DefinitionLocation, |
583 | StringHandle DefinitionSourceLine, |
584 | InclusionPathHandle InclusionPathHandle) |
585 | : MacroUnexpanded(MacroUnexpanded), |
586 | InstanceSourceLine(InstanceSourceLine) { |
587 | addMacroExpansionInstance(MacroExpanded, DefinitionLocation, |
588 | DefinitionSourceLine, InclusionPathHandle); |
589 | } |
590 | MacroExpansionTracker() {} |
591 | |
592 | // Find a matching macro expansion instance. |
593 | MacroExpansionInstance * |
594 | findMacroExpansionInstance(StringHandle MacroExpanded, |
595 | PPItemKey &DefinitionLocation) { |
596 | for (auto I = MacroExpansionInstances.begin(), |
597 | E = MacroExpansionInstances.end(); |
598 | I != E; ++I) { |
599 | if ((I->MacroExpanded == MacroExpanded) && |
600 | (I->DefinitionLocation == DefinitionLocation)) { |
601 | return &*I; // Found. |
602 | } |
603 | } |
604 | return nullptr; // Not found. |
605 | } |
606 | |
607 | // Add a macro expansion instance. |
608 | void addMacroExpansionInstance(StringHandle MacroExpanded, |
609 | PPItemKey &DefinitionLocation, |
610 | StringHandle DefinitionSourceLine, |
611 | InclusionPathHandle InclusionPathHandle) { |
612 | MacroExpansionInstances.push_back( |
613 | x: MacroExpansionInstance(MacroExpanded, DefinitionLocation, |
614 | DefinitionSourceLine, InclusionPathHandle)); |
615 | } |
616 | |
617 | // Return true if there is a mismatch. |
618 | bool hasMismatch() { return MacroExpansionInstances.size() > 1; } |
619 | |
620 | // A string representing the macro instance without expansion. |
621 | StringHandle MacroUnexpanded; |
622 | // A place to save the macro instance source line string. |
623 | StringHandle InstanceSourceLine; |
624 | // The macro expansion instances. |
625 | // If all instances of the macro expansion expand to the same value, |
626 | // This vector will only have one instance. |
627 | std::vector<MacroExpansionInstance> MacroExpansionInstances; |
628 | }; |
629 | |
630 | // Conditional expansion instance. |
631 | // |
632 | // This class represents an instance of a condition exoression result |
633 | // with a unique value. It also stores the unique header inclusion paths |
634 | // for use in telling the user the nested include path to the header. |
635 | class ConditionalExpansionInstance { |
636 | public: |
637 | ConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue, InclusionPathHandle H) |
638 | : ConditionValue(ConditionValue) { |
639 | InclusionPathHandles.push_back(x: H); |
640 | } |
641 | ConditionalExpansionInstance() {} |
642 | |
643 | // Check for the presence of a header inclusion path handle entry. |
644 | // Return false if not found. |
645 | bool haveInclusionPathHandle(InclusionPathHandle H) { |
646 | for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end(); |
647 | I != E; ++I) { |
648 | if (*I == H) |
649 | return true; |
650 | } |
651 | return InclusionPathHandleInvalid; |
652 | } |
653 | // Add a new header inclusion path entry, if not already present. |
654 | void addInclusionPathHandle(InclusionPathHandle H) { |
655 | if (!haveInclusionPathHandle(H)) |
656 | InclusionPathHandles.push_back(x: H); |
657 | } |
658 | |
659 | // A flag representing the evaluated condition value. |
660 | clang::PPCallbacks::ConditionValueKind ConditionValue; |
661 | // The header inclusion path handles for all the instances. |
662 | std::vector<InclusionPathHandle> InclusionPathHandles; |
663 | }; |
664 | |
665 | // Conditional directive instance tracker. |
666 | // |
667 | // This class represents one conditional directive, keyed by a PPItemKey. |
668 | // It stores a string representing the macro reference in the source, |
669 | // and a list of ConditionExpansionInstance objects representing |
670 | // the unique value the condition expression expands to in instances of |
671 | // the header. |
672 | class ConditionalTracker { |
673 | public: |
674 | ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind, |
675 | clang::PPCallbacks::ConditionValueKind ConditionValue, |
676 | StringHandle ConditionUnexpanded, |
677 | InclusionPathHandle InclusionPathHandle) |
678 | : DirectiveKind(DirectiveKind), ConditionUnexpanded(ConditionUnexpanded) { |
679 | addConditionalExpansionInstance(ConditionValue, InclusionPathHandle); |
680 | } |
681 | ConditionalTracker() {} |
682 | |
683 | // Find a matching condition expansion instance. |
684 | ConditionalExpansionInstance * |
685 | findConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue) { |
686 | for (auto I = ConditionalExpansionInstances.begin(), |
687 | E = ConditionalExpansionInstances.end(); |
688 | I != E; ++I) { |
689 | if (I->ConditionValue == ConditionValue) { |
690 | return &*I; // Found. |
691 | } |
692 | } |
693 | return nullptr; // Not found. |
694 | } |
695 | |
696 | // Add a conditional expansion instance. |
697 | void |
698 | addConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue, |
699 | InclusionPathHandle InclusionPathHandle) { |
700 | ConditionalExpansionInstances.push_back( |
701 | x: ConditionalExpansionInstance(ConditionValue, InclusionPathHandle)); |
702 | } |
703 | |
704 | // Return true if there is a mismatch. |
705 | bool hasMismatch() { return ConditionalExpansionInstances.size() > 1; } |
706 | |
707 | // The kind of directive. |
708 | clang::tok::PPKeywordKind DirectiveKind; |
709 | // A string representing the macro instance without expansion. |
710 | StringHandle ConditionUnexpanded; |
711 | // The condition expansion instances. |
712 | // If all instances of the conditional expression expand to the same value, |
713 | // This vector will only have one instance. |
714 | std::vector<ConditionalExpansionInstance> ConditionalExpansionInstances; |
715 | }; |
716 | |
717 | class PreprocessorTrackerImpl; |
718 | |
719 | // Preprocessor callbacks for modularize. |
720 | // |
721 | // This class derives from the Clang PPCallbacks class to track preprocessor |
722 | // actions, such as changing files and handling preprocessor directives and |
723 | // macro expansions. It has to figure out when a new header file is entered |
724 | // and left, as the provided handler is not particularly clear about it. |
725 | class PreprocessorCallbacks : public clang::PPCallbacks { |
726 | public: |
727 | PreprocessorCallbacks(PreprocessorTrackerImpl &ppTracker, |
728 | clang::Preprocessor &PP, llvm::StringRef ) |
729 | : PPTracker(ppTracker), PP(PP), RootHeaderFile(rootHeaderFile) {} |
730 | ~PreprocessorCallbacks() override {} |
731 | |
732 | // Overridden handlers. |
733 | void |
734 | InclusionDirective(clang::SourceLocation HashLoc, |
735 | const clang::Token &IncludeTok, llvm::StringRef FileName, |
736 | bool IsAngled, clang::CharSourceRange FilenameRange, |
737 | clang::OptionalFileEntryRef File, |
738 | llvm::StringRef SearchPath, llvm::StringRef RelativePath, |
739 | const clang::Module *SuggestedModule, bool ModuleImported, |
740 | clang::SrcMgr::CharacteristicKind FileType) override; |
741 | void FileChanged(clang::SourceLocation Loc, |
742 | clang::PPCallbacks::FileChangeReason Reason, |
743 | clang::SrcMgr::CharacteristicKind FileType, |
744 | clang::FileID PrevFID = clang::FileID()) override; |
745 | void MacroExpands(const clang::Token &MacroNameTok, |
746 | const clang::MacroDefinition &MD, clang::SourceRange Range, |
747 | const clang::MacroArgs *Args) override; |
748 | void Defined(const clang::Token &MacroNameTok, |
749 | const clang::MacroDefinition &MD, |
750 | clang::SourceRange Range) override; |
751 | void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange, |
752 | clang::PPCallbacks::ConditionValueKind ConditionResult) override; |
753 | void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange, |
754 | clang::PPCallbacks::ConditionValueKind ConditionResult, |
755 | clang::SourceLocation IfLoc) override; |
756 | void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, |
757 | const clang::MacroDefinition &MD) override; |
758 | void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, |
759 | const clang::MacroDefinition &MD) override; |
760 | |
761 | private: |
762 | PreprocessorTrackerImpl &PPTracker; |
763 | clang::Preprocessor &PP; |
764 | std::string ; |
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: |
783 | PreprocessorTrackerImpl(llvm::SmallVector<std::string, 32> &, |
784 | bool ) |
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(Elt: getCanonicalPath(path: *I)); |
793 | } |
794 | } |
795 | |
796 | ~PreprocessorTrackerImpl() override {} |
797 | |
798 | // Handle entering a preprocessing session. |
799 | void handlePreprocessorEntry(clang::Preprocessor &PP, |
800 | llvm::StringRef ) override { |
801 | HeadersInThisCompile.clear(); |
802 | assert((HeaderStack.size() == 0) && "Header stack should be empty." ); |
803 | pushHeaderHandle(H: addHeader(HeaderPath: rootHeaderFile)); |
804 | PP.addPPCallbacks(C: std::make_unique<PreprocessorCallbacks>(args&: *this, args&: PP, |
805 | args&: rootHeaderFile)); |
806 | } |
807 | // Handle exiting a preprocessing session. |
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. |
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(HeaderPath: TargetPath)) |
820 | return; |
821 | HeaderHandle CurrentHeaderHandle = findHeaderHandle(HeaderPath: DirectivePath); |
822 | StringHandle IncludeHeaderHandle = addString(Str: 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(x: 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. |
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(SpellingLoc: BlockStartLoc); |
847 | std::string SourcePath = getSourceLocationFile(PP, Loc: BlockStartLoc); |
848 | SourcePath = ModularizeUtilities::getCanonicalPath(FilePath: SourcePath); |
849 | HeaderHandle SourceHandle = findHeaderHandle(HeaderPath: SourcePath); |
850 | if (SourceHandle == -1) |
851 | return true; |
852 | int BlockStartLine, BlockStartColumn, BlockEndLine, BlockEndColumn; |
853 | bool returnValue = true; |
854 | getSourceLocationLineAndColumn(PP, Loc: BlockStartLoc, Line&: BlockStartLine, |
855 | Column&: BlockStartColumn); |
856 | getSourceLocationLineAndColumn(PP, Loc: BlockEndLoc, Line&: BlockEndLine, |
857 | Column&: 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, Line: 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, Loc: 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. |
883 | void handleHeaderEntry(clang::Preprocessor &PP, llvm::StringRef ) { |
884 | // Ignore <built-in> and <command-line> to reduce message clutter. |
885 | if (HeaderPath.starts_with(Prefix: "<" )) |
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(V: H).second; |
893 | } |
894 | |
895 | // Handle exiting a header source file. |
896 | void handleHeaderExit(llvm::StringRef ) { |
897 | // Ignore <built-in> and <command-line> to reduce message clutter. |
898 | if (HeaderPath.starts_with(Prefix: "<" )) |
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. |
912 | StringHandle addString(llvm::StringRef Str) { |
913 | return Strings.insert(key: Str).first->first(); |
914 | } |
915 | |
916 | // Convert to a canonical path. |
917 | std::string getCanonicalPath(llvm::StringRef path) const { |
918 | std::string CanonicalPath(path); |
919 | std::replace(first: CanonicalPath.begin(), last: CanonicalPath.end(), old_value: '\\', new_value: '/'); |
920 | return CanonicalPath; |
921 | } |
922 | |
923 | // Return true if the given header is in the header list. |
924 | bool (llvm::StringRef ) const { |
925 | std::string CanonicalPath = getCanonicalPath(path: 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. |
937 | HeaderHandle findHeaderHandle(llvm::StringRef ) const { |
938 | std::string CanonicalPath = getCanonicalPath(path: 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. |
950 | HeaderHandle (llvm::StringRef ) { |
951 | std::string CanonicalPath = getCanonicalPath(path: HeaderPath); |
952 | HeaderHandle H = findHeaderHandle(HeaderPath: CanonicalPath); |
953 | if (H == HeaderHandleInvalid) { |
954 | H = HeaderPaths.size(); |
955 | HeaderPaths.push_back(x: addString(Str: CanonicalPath)); |
956 | } |
957 | return H; |
958 | } |
959 | |
960 | // Return a header file path string given its handle. |
961 | StringHandle (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. |
968 | InclusionPathHandle pushHeaderHandle(HeaderHandle H) { |
969 | HeaderStack.push_back(x: H); |
970 | return CurrentInclusionPathHandle = addInclusionPathHandle(Path: HeaderStack); |
971 | } |
972 | // Pops the last header handle from the stack; |
973 | void popHeaderHandle() { |
974 | // assert((HeaderStack.size() != 0) && "Header stack already empty."); |
975 | if (HeaderStack.size() != 0) { |
976 | HeaderStack.pop_back(); |
977 | CurrentInclusionPathHandle = addInclusionPathHandle(Path: HeaderStack); |
978 | } |
979 | } |
980 | // Get the top handle on the header stack. |
981 | 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. |
988 | bool isHeaderHandleInStack(HeaderHandle H) const { |
989 | return llvm::is_contained(Range: HeaderStack, Element: H); |
990 | } |
991 | |
992 | // Get the handle of a header inclusion path entry. |
993 | // Return InclusionPathHandleInvalid if not found. |
994 | InclusionPathHandle |
995 | findInclusionPathHandle(const std::vector<HeaderHandle> &Path) const { |
996 | InclusionPathHandle H = 0; |
997 | for (auto I = InclusionPaths.begin(), E = InclusionPaths.end(); I != E; |
998 | ++I, ++H) { |
999 | if (I->Path == Path) |
1000 | return H; |
1001 | } |
1002 | return HeaderHandleInvalid; |
1003 | } |
1004 | // Add a new header inclusion path entry, or return existing handle. |
1005 | // Return the header inclusion path entry handle. |
1006 | InclusionPathHandle |
1007 | addInclusionPathHandle(const std::vector<HeaderHandle> &Path) { |
1008 | InclusionPathHandle H = findInclusionPathHandle(Path); |
1009 | if (H == HeaderHandleInvalid) { |
1010 | H = InclusionPaths.size(); |
1011 | InclusionPaths.push_back(x: HeaderInclusionPath(Path)); |
1012 | } |
1013 | return H; |
1014 | } |
1015 | // Return the current inclusion path handle. |
1016 | InclusionPathHandle getCurrentInclusionPathHandle() const { |
1017 | return CurrentInclusionPathHandle; |
1018 | } |
1019 | |
1020 | // Return an inclusion path given its handle. |
1021 | const std::vector<HeaderHandle> & |
1022 | getInclusionPath(InclusionPathHandle H) const { |
1023 | if ((H >= 0) && (H <= (InclusionPathHandle)InclusionPaths.size())) |
1024 | return InclusionPaths[H].Path; |
1025 | static std::vector<HeaderHandle> Empty; |
1026 | return Empty; |
1027 | } |
1028 | |
1029 | // Add a macro expansion instance. |
1030 | void addMacroExpansionInstance(clang::Preprocessor &PP, HeaderHandle H, |
1031 | clang::SourceLocation InstanceLoc, |
1032 | clang::SourceLocation DefinitionLoc, |
1033 | clang::IdentifierInfo *II, |
1034 | llvm::StringRef MacroUnexpanded, |
1035 | llvm::StringRef MacroExpanded, |
1036 | InclusionPathHandle InclusionPathHandle) { |
1037 | if (InNestedHeader) |
1038 | return; |
1039 | StringHandle MacroName = addString(Str: II->getName()); |
1040 | PPItemKey InstanceKey(PP, MacroName, H, InstanceLoc); |
1041 | PPItemKey DefinitionKey(PP, MacroName, H, DefinitionLoc); |
1042 | auto I = MacroExpansions.find(x: InstanceKey); |
1043 | // If existing instance of expansion not found, add one. |
1044 | if (I == MacroExpansions.end()) { |
1045 | std::string InstanceSourceLine = |
1046 | getSourceLocationString(PP, Loc: InstanceLoc) + ":\n" + |
1047 | getSourceLine(PP, Loc: InstanceLoc) + "\n" ; |
1048 | std::string DefinitionSourceLine = |
1049 | getSourceLocationString(PP, Loc: DefinitionLoc) + ":\n" + |
1050 | getSourceLine(PP, Loc: DefinitionLoc) + "\n" ; |
1051 | MacroExpansions[InstanceKey] = MacroExpansionTracker( |
1052 | addString(Str: MacroUnexpanded), addString(Str: MacroExpanded), |
1053 | addString(Str: InstanceSourceLine), DefinitionKey, |
1054 | addString(Str: DefinitionSourceLine), InclusionPathHandle); |
1055 | } else { |
1056 | // We've seen the macro before. Get its tracker. |
1057 | MacroExpansionTracker &CondTracker = I->second; |
1058 | // Look up an existing instance value for the macro. |
1059 | MacroExpansionInstance *MacroInfo = |
1060 | CondTracker.findMacroExpansionInstance(MacroExpanded: addString(Str: MacroExpanded), |
1061 | DefinitionLocation&: DefinitionKey); |
1062 | // If found, just add the inclusion path to the instance. |
1063 | if (MacroInfo) |
1064 | MacroInfo->addInclusionPathHandle(H: InclusionPathHandle); |
1065 | else { |
1066 | // Otherwise add a new instance with the unique value. |
1067 | std::string DefinitionSourceLine = |
1068 | getSourceLocationString(PP, Loc: DefinitionLoc) + ":\n" + |
1069 | getSourceLine(PP, Loc: DefinitionLoc) + "\n" ; |
1070 | CondTracker.addMacroExpansionInstance( |
1071 | MacroExpanded: addString(Str: MacroExpanded), DefinitionLocation&: DefinitionKey, |
1072 | DefinitionSourceLine: addString(Str: DefinitionSourceLine), InclusionPathHandle); |
1073 | } |
1074 | } |
1075 | } |
1076 | |
1077 | // Add a conditional expansion instance. |
1078 | void |
1079 | addConditionalExpansionInstance(clang::Preprocessor &PP, HeaderHandle H, |
1080 | clang::SourceLocation InstanceLoc, |
1081 | clang::tok::PPKeywordKind DirectiveKind, |
1082 | clang::PPCallbacks::ConditionValueKind ConditionValue, |
1083 | llvm::StringRef ConditionUnexpanded, |
1084 | InclusionPathHandle InclusionPathHandle) { |
1085 | // Ignore header guards, assuming the header guard is the only conditional. |
1086 | if (InNestedHeader) |
1087 | return; |
1088 | StringHandle ConditionUnexpandedHandle(addString(Str: ConditionUnexpanded)); |
1089 | PPItemKey InstanceKey(PP, ConditionUnexpandedHandle, H, InstanceLoc); |
1090 | auto I = ConditionalExpansions.find(x: InstanceKey); |
1091 | // If existing instance of condition not found, add one. |
1092 | if (I == ConditionalExpansions.end()) { |
1093 | std::string InstanceSourceLine = |
1094 | getSourceLocationString(PP, Loc: InstanceLoc) + ":\n" + |
1095 | getSourceLine(PP, Loc: InstanceLoc) + "\n" ; |
1096 | ConditionalExpansions[InstanceKey] = |
1097 | ConditionalTracker(DirectiveKind, ConditionValue, |
1098 | ConditionUnexpandedHandle, InclusionPathHandle); |
1099 | } else { |
1100 | // We've seen the conditional before. Get its tracker. |
1101 | ConditionalTracker &CondTracker = I->second; |
1102 | // Look up an existing instance value for the condition. |
1103 | ConditionalExpansionInstance *MacroInfo = |
1104 | CondTracker.findConditionalExpansionInstance(ConditionValue); |
1105 | // If found, just add the inclusion path to the instance. |
1106 | if (MacroInfo) |
1107 | MacroInfo->addInclusionPathHandle(H: InclusionPathHandle); |
1108 | else { |
1109 | // Otherwise add a new instance with the unique value. |
1110 | CondTracker.addConditionalExpansionInstance(ConditionValue, |
1111 | InclusionPathHandle); |
1112 | } |
1113 | } |
1114 | } |
1115 | |
1116 | // Report on inconsistent macro instances. |
1117 | // Returns true if any mismatches. |
1118 | bool reportInconsistentMacros(llvm::raw_ostream &OS) override { |
1119 | bool ReturnValue = false; |
1120 | // Walk all the macro expansion trackers in the map. |
1121 | for (auto I = MacroExpansions.begin(), E = MacroExpansions.end(); I != E; |
1122 | ++I) { |
1123 | const PPItemKey &ItemKey = I->first; |
1124 | MacroExpansionTracker &MacroExpTracker = I->second; |
1125 | // If no mismatch (only one instance value) continue. |
1126 | if (!MacroExpTracker.hasMismatch()) |
1127 | continue; |
1128 | // Tell caller we found one or more errors. |
1129 | ReturnValue = true; |
1130 | // Start the error message. |
1131 | OS << MacroExpTracker.InstanceSourceLine; |
1132 | if (ItemKey.Column > 0) |
1133 | OS << std::string(ItemKey.Column - 1, ' ') << "^\n" ; |
1134 | OS << "error: Macro instance '" << MacroExpTracker.MacroUnexpanded |
1135 | << "' has different values in this header, depending on how it was " |
1136 | "included.\n" ; |
1137 | // Walk all the instances. |
1138 | for (auto IMT = MacroExpTracker.MacroExpansionInstances.begin(), |
1139 | EMT = MacroExpTracker.MacroExpansionInstances.end(); |
1140 | IMT != EMT; ++IMT) { |
1141 | MacroExpansionInstance &MacroInfo = *IMT; |
1142 | OS << " '" << MacroExpTracker.MacroUnexpanded << "' expanded to: '" |
1143 | << MacroInfo.MacroExpanded |
1144 | << "' with respect to these inclusion paths:\n" ; |
1145 | // Walk all the inclusion path hierarchies. |
1146 | for (auto IIP = MacroInfo.InclusionPathHandles.begin(), |
1147 | EIP = MacroInfo.InclusionPathHandles.end(); |
1148 | IIP != EIP; ++IIP) { |
1149 | const std::vector<HeaderHandle> &ip = getInclusionPath(H: *IIP); |
1150 | auto Count = (int)ip.size(); |
1151 | for (int Index = 0; Index < Count; ++Index) { |
1152 | HeaderHandle H = ip[Index]; |
1153 | OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H) |
1154 | << "\n" ; |
1155 | } |
1156 | } |
1157 | // For a macro that wasn't defined, we flag it by using the |
1158 | // instance location. |
1159 | // If there is a definition... |
1160 | if (MacroInfo.DefinitionLocation.Line != ItemKey.Line) { |
1161 | OS << MacroInfo.DefinitionSourceLine; |
1162 | if (MacroInfo.DefinitionLocation.Column > 0) |
1163 | OS << std::string(MacroInfo.DefinitionLocation.Column - 1, ' ') |
1164 | << "^\n" ; |
1165 | OS << "Macro defined here.\n" ; |
1166 | } else |
1167 | OS << "(no macro definition)" |
1168 | << "\n" ; |
1169 | } |
1170 | } |
1171 | return ReturnValue; |
1172 | } |
1173 | |
1174 | // Report on inconsistent conditional instances. |
1175 | // Returns true if any mismatches. |
1176 | bool reportInconsistentConditionals(llvm::raw_ostream &OS) override { |
1177 | bool ReturnValue = false; |
1178 | // Walk all the conditional trackers in the map. |
1179 | for (auto I = ConditionalExpansions.begin(), |
1180 | E = ConditionalExpansions.end(); |
1181 | I != E; ++I) { |
1182 | const PPItemKey &ItemKey = I->first; |
1183 | ConditionalTracker &CondTracker = I->second; |
1184 | if (!CondTracker.hasMismatch()) |
1185 | continue; |
1186 | // Tell caller we found one or more errors. |
1187 | ReturnValue = true; |
1188 | // Start the error message. |
1189 | OS << HeaderPaths[ItemKey.File] << ":" << ItemKey.Line << ":" |
1190 | << ItemKey.Column << "\n" ; |
1191 | OS << "#" << getDirectiveSpelling(kind: CondTracker.DirectiveKind) << " " |
1192 | << CondTracker.ConditionUnexpanded << "\n" ; |
1193 | OS << "^\n" ; |
1194 | OS << "error: Conditional expression instance '" |
1195 | << CondTracker.ConditionUnexpanded |
1196 | << "' has different values in this header, depending on how it was " |
1197 | "included.\n" ; |
1198 | // Walk all the instances. |
1199 | for (auto IMT = CondTracker.ConditionalExpansionInstances.begin(), |
1200 | EMT = CondTracker.ConditionalExpansionInstances.end(); |
1201 | IMT != EMT; ++IMT) { |
1202 | ConditionalExpansionInstance &MacroInfo = *IMT; |
1203 | OS << " '" << CondTracker.ConditionUnexpanded << "' expanded to: '" |
1204 | << ConditionValueKindStrings[MacroInfo.ConditionValue] |
1205 | << "' with respect to these inclusion paths:\n" ; |
1206 | // Walk all the inclusion path hierarchies. |
1207 | for (auto IIP = MacroInfo.InclusionPathHandles.begin(), |
1208 | EIP = MacroInfo.InclusionPathHandles.end(); |
1209 | IIP != EIP; ++IIP) { |
1210 | const std::vector<HeaderHandle> &ip = getInclusionPath(H: *IIP); |
1211 | auto Count = (int)ip.size(); |
1212 | for (int Index = 0; Index < Count; ++Index) { |
1213 | HeaderHandle H = ip[Index]; |
1214 | OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H) |
1215 | << "\n" ; |
1216 | } |
1217 | } |
1218 | } |
1219 | } |
1220 | return ReturnValue; |
1221 | } |
1222 | |
1223 | // Get directive spelling. |
1224 | static const char *getDirectiveSpelling(clang::tok::PPKeywordKind kind) { |
1225 | switch (kind) { |
1226 | case clang::tok::pp_if: |
1227 | return "if" ; |
1228 | case clang::tok::pp_elif: |
1229 | return "elif" ; |
1230 | case clang::tok::pp_ifdef: |
1231 | return "ifdef" ; |
1232 | case clang::tok::pp_ifndef: |
1233 | return "ifndef" ; |
1234 | default: |
1235 | return "(unknown)" ; |
1236 | } |
1237 | } |
1238 | |
1239 | private: |
1240 | llvm::SmallVector<std::string, 32> ; |
1241 | // Only do extern, namespace check for headers in HeaderList. |
1242 | bool ; |
1243 | llvm::StringSet<> Strings; |
1244 | std::vector<StringHandle> ; |
1245 | std::vector<HeaderHandle> ; |
1246 | std::vector<HeaderInclusionPath> InclusionPaths; |
1247 | InclusionPathHandle CurrentInclusionPathHandle; |
1248 | llvm::SmallSet<HeaderHandle, 32> ; |
1249 | std::vector<PPItemKey> IncludeDirectives; |
1250 | MacroExpansionMap MacroExpansions; |
1251 | ConditionalExpansionMap ConditionalExpansions; |
1252 | bool ; |
1253 | }; |
1254 | |
1255 | } // namespace |
1256 | |
1257 | // PreprocessorTracker functions. |
1258 | |
1259 | // PreprocessorTracker destructor. |
1260 | PreprocessorTracker::~PreprocessorTracker() {} |
1261 | |
1262 | // Create instance of PreprocessorTracker. |
1263 | PreprocessorTracker *PreprocessorTracker::create( |
1264 | llvm::SmallVector<std::string, 32> &, |
1265 | bool ) { |
1266 | return new PreprocessorTrackerImpl(Headers, DoBlockCheckHeaderListOnly); |
1267 | } |
1268 | |
1269 | // Preprocessor callbacks for modularize. |
1270 | |
1271 | // Handle include directive. |
1272 | void PreprocessorCallbacks::InclusionDirective( |
1273 | clang::SourceLocation HashLoc, const clang::Token &IncludeTok, |
1274 | llvm::StringRef FileName, bool IsAngled, |
1275 | clang::CharSourceRange FilenameRange, clang::OptionalFileEntryRef File, |
1276 | llvm::StringRef SearchPath, llvm::StringRef RelativePath, |
1277 | const clang::Module *SuggestedModule, bool ModuleImported, |
1278 | clang::SrcMgr::CharacteristicKind FileType) { |
1279 | int DirectiveLine, DirectiveColumn; |
1280 | std::string = getSourceLocationFile(PP, Loc: HashLoc); |
1281 | getSourceLocationLineAndColumn(PP, Loc: HashLoc, Line&: DirectiveLine, Column&: DirectiveColumn); |
1282 | PPTracker.handleIncludeDirective(DirectivePath: HeaderPath, DirectiveLine, DirectiveColumn, |
1283 | TargetPath: FileName); |
1284 | } |
1285 | |
1286 | // Handle file entry/exit. |
1287 | void PreprocessorCallbacks::FileChanged( |
1288 | clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason, |
1289 | clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) { |
1290 | switch (Reason) { |
1291 | case EnterFile: |
1292 | PPTracker.handleHeaderEntry(PP, HeaderPath: getSourceLocationFile(PP, Loc)); |
1293 | break; |
1294 | case ExitFile: { |
1295 | clang::OptionalFileEntryRef F = |
1296 | PP.getSourceManager().getFileEntryRefForID(FID: PrevFID); |
1297 | if (F) |
1298 | PPTracker.handleHeaderExit(HeaderPath: F->getName()); |
1299 | } break; |
1300 | case SystemHeaderPragma: |
1301 | case RenameFile: |
1302 | break; |
1303 | } |
1304 | } |
1305 | |
1306 | // Handle macro expansion. |
1307 | void PreprocessorCallbacks::MacroExpands(const clang::Token &MacroNameTok, |
1308 | const clang::MacroDefinition &MD, |
1309 | clang::SourceRange Range, |
1310 | const clang::MacroArgs *Args) { |
1311 | clang::SourceLocation Loc = Range.getBegin(); |
1312 | // Ignore macro argument expansions. |
1313 | if (!Loc.isFileID()) |
1314 | return; |
1315 | clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo(); |
1316 | const clang::MacroInfo *MI = MD.getMacroInfo(); |
1317 | std::string MacroName = II->getName().str(); |
1318 | std::string Unexpanded(getMacroUnexpandedString(Range, PP, MacroName, MI)); |
1319 | std::string Expanded(getMacroExpandedString(PP, MacroName, MI, Args)); |
1320 | PPTracker.addMacroExpansionInstance( |
1321 | PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc, DefinitionLoc: MI->getDefinitionLoc(), II, |
1322 | MacroUnexpanded: Unexpanded, MacroExpanded: Expanded, InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle()); |
1323 | } |
1324 | |
1325 | void PreprocessorCallbacks::Defined(const clang::Token &MacroNameTok, |
1326 | const clang::MacroDefinition &MD, |
1327 | clang::SourceRange Range) { |
1328 | clang::SourceLocation Loc(Range.getBegin()); |
1329 | clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo(); |
1330 | const clang::MacroInfo *MI = MD.getMacroInfo(); |
1331 | std::string MacroName = II->getName().str(); |
1332 | std::string Unexpanded(getSourceString(PP, Range)); |
1333 | PPTracker.addMacroExpansionInstance( |
1334 | PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc, |
1335 | DefinitionLoc: (MI ? MI->getDefinitionLoc() : Loc), II, MacroUnexpanded: Unexpanded, |
1336 | MacroExpanded: (MI ? "true" : "false" ), InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle()); |
1337 | } |
1338 | |
1339 | void PreprocessorCallbacks::If(clang::SourceLocation Loc, |
1340 | clang::SourceRange ConditionRange, |
1341 | clang::PPCallbacks::ConditionValueKind ConditionResult) { |
1342 | std::string Unexpanded(getSourceString(PP, Range: ConditionRange)); |
1343 | PPTracker.addConditionalExpansionInstance( |
1344 | PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc, DirectiveKind: clang::tok::pp_if, |
1345 | ConditionValue: ConditionResult, ConditionUnexpanded: Unexpanded, InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle()); |
1346 | } |
1347 | |
1348 | void PreprocessorCallbacks::Elif(clang::SourceLocation Loc, |
1349 | clang::SourceRange ConditionRange, |
1350 | clang::PPCallbacks::ConditionValueKind ConditionResult, |
1351 | clang::SourceLocation IfLoc) { |
1352 | std::string Unexpanded(getSourceString(PP, Range: ConditionRange)); |
1353 | PPTracker.addConditionalExpansionInstance( |
1354 | PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc, DirectiveKind: clang::tok::pp_elif, |
1355 | ConditionValue: ConditionResult, ConditionUnexpanded: Unexpanded, InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle()); |
1356 | } |
1357 | |
1358 | void PreprocessorCallbacks::Ifdef(clang::SourceLocation Loc, |
1359 | const clang::Token &MacroNameTok, |
1360 | const clang::MacroDefinition &MD) { |
1361 | clang::PPCallbacks::ConditionValueKind IsDefined = |
1362 | (MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False ); |
1363 | PPTracker.addConditionalExpansionInstance( |
1364 | PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc, DirectiveKind: clang::tok::pp_ifdef, |
1365 | ConditionValue: IsDefined, ConditionUnexpanded: PP.getSpelling(Tok: MacroNameTok), |
1366 | InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle()); |
1367 | } |
1368 | |
1369 | void PreprocessorCallbacks::Ifndef(clang::SourceLocation Loc, |
1370 | const clang::Token &MacroNameTok, |
1371 | const clang::MacroDefinition &MD) { |
1372 | clang::PPCallbacks::ConditionValueKind IsNotDefined = |
1373 | (!MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False ); |
1374 | PPTracker.addConditionalExpansionInstance( |
1375 | PP, H: PPTracker.getCurrentHeaderHandle(), InstanceLoc: Loc, DirectiveKind: clang::tok::pp_ifndef, |
1376 | ConditionValue: IsNotDefined, ConditionUnexpanded: PP.getSpelling(Tok: MacroNameTok), |
1377 | InclusionPathHandle: PPTracker.getCurrentInclusionPathHandle()); |
1378 | } |
1379 | } // end namespace Modularize |
1380 | |