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 | 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. |
401 | static std::string getMacroExpandedString(clang::Preprocessor &PP, |
402 | llvm::StringRef MacroName, |
403 | const clang::MacroInfo *MI, |
404 | const clang::MacroArgs *Args) { |
405 | std::string Expanded; |
406 | // Walk over the macro Tokens. |
407 | for (const auto &T : MI->tokens()) { |
408 | clang::IdentifierInfo *II = T.getIdentifierInfo(); |
409 | int ArgNo = (II && Args ? MI->getParameterNum(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 | |
458 | namespace { |
459 | |
460 | // ConditionValueKind strings. |
461 | const char * |
462 | ConditionValueKindStrings[] = { |
463 | "(not evaluated)", "false", "true" |
464 | }; |
465 | |
466 | // Preprocessor item key. |
467 | // |
468 | // This class represents a location in a source file, for use |
469 | // as a key representing a unique name/file/line/column quadruplet, |
470 | // which in this case is used to identify a macro expansion instance, |
471 | // but could be used for other things as well. |
472 | // The file is a header file handle, the line is a line number, |
473 | // and the column is a column number. |
474 | class PPItemKey { |
475 | public: |
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. |
507 | class HeaderInclusionPath { |
508 | public: |
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. |
521 | class MacroExpansionInstance { |
522 | public: |
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. |
565 | class MacroExpansionTracker { |
566 | public: |
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. |
623 | class ConditionalExpansionInstance { |
624 | public: |
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. |
660 | class ConditionalTracker { |
661 | public: |
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 | |
705 | class 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. |
713 | class PreprocessorCallbacks : public clang::PPCallbacks { |
714 | public: |
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 | |
749 | private: |
750 | PreprocessorTrackerImpl &PPTracker; |
751 | clang::Preprocessor &PP; |
752 | std::string RootHeaderFile; |
753 | }; |
754 | |
755 | // Preprocessor macro expansion item map types. |
756 | typedef std::map<PPItemKey, MacroExpansionTracker> MacroExpansionMap; |
757 | typedef std::map<PPItemKey, MacroExpansionTracker>::iterator |
758 | MacroExpansionMapIter; |
759 | |
760 | // Preprocessor conditional expansion item map types. |
761 | typedef std::map<PPItemKey, ConditionalTracker> ConditionalExpansionMap; |
762 | typedef std::map<PPItemKey, ConditionalTracker>::iterator |
763 | ConditionalExpansionMapIter; |
764 | |
765 | // Preprocessor tracker for modularize. |
766 | // |
767 | // This class stores information about all the headers processed in the |
768 | // course of running modularize. |
769 | class PreprocessorTrackerImpl : public PreprocessorTracker { |
770 | public: |
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 | |
1227 | private: |
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. |
1248 | PreprocessorTracker::~PreprocessorTracker() {} |
1249 | |
1250 | // Create instance of PreprocessorTracker. |
1251 | PreprocessorTracker *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. |
1260 | void 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. |
1275 | void 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. |
1295 | void 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 | |
1313 | void 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 | |
1326 | void 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 | |
1335 | void 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 | |
1345 | void 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 | |
1356 | void 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 |
Definitions
- HeaderHandleInvalid
- InclusionPathHandleInvalid
- getSourceLocationString
- getSourceLocationFile
- getSourceLocationLineAndColumn
- getSourceString
- getSourceLine
- getSourceLine
- getMacroUnexpandedString
- getMacroExpandedString
- ConditionValueKindStrings
- PPItemKey
- PPItemKey
- PPItemKey
- PPItemKey
- PPItemKey
- operator==
- operator<
- HeaderInclusionPath
- HeaderInclusionPath
- HeaderInclusionPath
- HeaderInclusionPath
- MacroExpansionInstance
- MacroExpansionInstance
- MacroExpansionInstance
- haveInclusionPathHandle
- addInclusionPathHandle
- MacroExpansionTracker
- MacroExpansionTracker
- MacroExpansionTracker
- findMacroExpansionInstance
- addMacroExpansionInstance
- hasMismatch
- ConditionalExpansionInstance
- ConditionalExpansionInstance
- ConditionalExpansionInstance
- haveInclusionPathHandle
- addInclusionPathHandle
- ConditionalTracker
- ConditionalTracker
- ConditionalTracker
- findConditionalExpansionInstance
- addConditionalExpansionInstance
- hasMismatch
- PreprocessorCallbacks
- PreprocessorCallbacks
- ~PreprocessorCallbacks
- PreprocessorTrackerImpl
- PreprocessorTrackerImpl
- ~PreprocessorTrackerImpl
- handlePreprocessorEntry
- handlePreprocessorExit
- handleIncludeDirective
- checkForIncludesInBlock
- handleHeaderEntry
- handleHeaderExit
- addString
- getCanonicalPath
- isHeaderListHeader
- findHeaderHandle
- addHeader
- getHeaderFilePath
- pushHeaderHandle
- popHeaderHandle
- getCurrentHeaderHandle
- isHeaderHandleInStack
- findInclusionPathHandle
- addInclusionPathHandle
- getCurrentInclusionPathHandle
- getInclusionPath
- addMacroExpansionInstance
- addConditionalExpansionInstance
- reportInconsistentMacros
- reportInconsistentConditionals
- getDirectiveSpelling
- ~PreprocessorTracker
- create
- InclusionDirective
- FileChanged
- MacroExpands
- Defined
- If
- Elif
- Ifdef
Improve your Profiling and Debugging skills
Find out more