forked from include-what-you-use/include-what-you-use
-
Notifications
You must be signed in to change notification settings - Fork 5
/
iwyu_preprocessor.h
389 lines (328 loc) · 17.4 KB
/
iwyu_preprocessor.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
//===--- iwyu_preprocessor.h - handle #includes/#defines for iwyu ---------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// The class that gets preprocessor callbacks: #includes, #defines,
// #pragmas, and the like.
//
// It has three main tasks in iwyu:
//
// 1) Record the #include hierarchy. For each file it sees, it creates
// an IwyuOutput class for that file, which is set up to hold the direct
// includes that the file has.
//
// 2) Record iwyu violations for macro uses. In particular, if a macro
// is #defined in one file and the token is accessed in another, do an
// iwyu check on that use.
//
// 3) Process iwyu pragma-like constructs. Comments beginning "//"
// are allowed to follow any pragma, otherwise extraneous text on the
// line will result in an error message being logged. Here are the
// constructs we look for:
// Full-line constructs:
// a) // IWYU pragma: private, include "foo/bar/baz.h"
// b) // IWYU pragma: private
// c) // IWYU pragma: begin_exports
// d) // IWYU pragma: end_exports
// e) // IWYU pragma: no_include "foo/bar/baz.h"
// f) // IWYU pragma: no_forward_declare foo::Bar
// g) // IWYU pragma: friend <regexp>
// // IWYU pragma: friend "<regexp>" -- needed if spaces in regexp.
// h) // IWYU pragma: begin_keep
// i) // IWYU pragma: end_keep
// 'Annotation' constructs:
// h) #include "foo/bar/baz.h" // IWYU pragma: export
// i) #include "foo/bar/baz.h" // IWYU pragma: keep
//
// 4) Process doxygen @headername directives. In later versions of GCC,
// these directives are like IWYU pragma private directives:
// @headername{foo} means to include <foo> instead.
// The arguments are allowed to be a comma-separated list.
// See
// http://gcc.gnu.org/onlinedocs/libstdc++/manual/documentation_hacking.html
//
// This class finishes its processing before the 'main' iwyu
// processing is done, so other iwyu consumers can access the main
// outputs of this class:
// * The map from include-name to FileEntry*.
// * The map from FileEntry* to its IwyuFileInfo object.
// * TODO(csilvers): Information about direct includes of a FileEntry*
// * The 'intends to provide' map, which encapsulates some
// of the information about public vs private headers.
// * Testing and reporting membership in the main compilation unit.
#ifndef INCLUDE_WHAT_YOU_USE_IWYU_PREPROCESSOR_H_
#define INCLUDE_WHAT_YOU_USE_IWYU_PREPROCESSOR_H_
#include <map> // for map
#include <set> // for set
#include <stack> // for stack
#include <string> // for string
#include <utility> // for pair
#include <vector> // for vector
#include "iwyu_output.h"
#include "iwyu_port.h" // for CHECK_
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Token.h"
namespace clang {
class FileEntry;
class MacroInfo;
class NamedDecl;
} // namespace clang
namespace include_what_you_use {
using std::map;
using std::set;
using std::stack;
using std::string;
using std::vector;
using std::multimap;
class IwyuPreprocessorInfo : public clang::PPCallbacks,
public clang::CommentHandler {
public:
IwyuPreprocessorInfo() : main_file_(nullptr),
empty_file_info_(nullptr, this, "") {}
// The client *must* call this from the beginning of HandleTranslationUnit()
void HandlePreprocessingDone();
// More direct ways of getting at this information
const clang::FileEntry* main_file() const {
return main_file_;
}
const set<const clang::FileEntry*>* files_to_report_iwyu_violations_for()
const {
return &files_to_report_iwyu_violations_for_;
}
// Given a quoted include like '<vector>', or '"ads/base.h"',
// returns the FileEntry for that file, or nullptr if none is
// found. If multiple files are included under the same
// quoted-include name (which can happen via #include-next),
// one is returned arbitrarily. (But always the same one.)
const clang::FileEntry* IncludeToFileEntry(const string quoted_include) const;
// Returns an IwyuFileInfo struct (from iwyu_output.h) corresponding
// to the given file, or nullptr if no such struct can be found.
// Note this is a const method that returns a non-const pointer.
// Be careful if using this method in threaded contexts.
IwyuFileInfo* FileInfoFor(const clang::FileEntry* file) const;
// Instead of returning nullptr if no file info can be found, returns
// an empty IwyuFileInfo struct.
const IwyuFileInfo& FileInfoOrEmptyFor(const clang::FileEntry* file) const;
// For every file we've seen (that is, that we've #included),
// returns what files it 'intends' to provide full type information
// for. The motivation is that a file like <vector> #includes
// <memory> and doesn't expect you to have to, even though
// technically it's required whenever you create a vector<Foo>,
// which is really vector<Foo, alloc<Foo>>. We say you don't have
// to #include <memory> because vector intends to provide the full
// types from <memory> for you.
// The rule we use is every file intends to provide full type
// information for the files it directly includes. For public
// header files -- ones that the include-picker can map another file
// to -- we relax the rule to say the public header intends to
// provide *every* header file behind it, direct or no. This scheme
// isn't perfect -- it says <map> intends to provide the full type
// of pair<>, when really it just uses it internally -- but it's a
// reasonable heuristic.
// Returns true iff our analysis shows that public_header intends
// to provide all the symbols in other_file.
bool PublicHeaderIntendsToProvide(const clang::FileEntry* public_header,
const clang::FileEntry* other_file) const;
// Returns true if the first file directly or indirectly includes
// the second.
bool FileTransitivelyIncludes(const clang::FileEntry* includer,
const clang::FileEntry* includee) const;
bool FileTransitivelyIncludes(const clang::FileEntry* includer,
const string& quoted_includee) const;
// This seems like a weird way to call this function, but we
// happen to need this in iwyu_output.cc:
bool FileTransitivelyIncludes(const string& quoted_includer,
const clang::FileEntry* includee) const;
// Return true if the given file has
// "// IWYU pragma: no_include <other_filename>".
bool IncludeIsInhibited(const clang::FileEntry* file,
const string& other_filename) const;
// Return true if the given file has
// "// IWYU pragma: no_forward_declare <qualified_symbol_name>".
bool ForwardDeclareIsInhibited(
const clang::FileEntry* file, const string& qualified_symbol_name) const;
// Return true if the fwd decl is marked with "IWYU pragma: keep".
bool ForwardDeclareIsMarkedKeep(const clang::NamedDecl* decl) const;
// Return true if the fwd decl is marked with "IWYU pragma: export".
bool ForwardDeclareIsExported(const clang::NamedDecl* decl) const;
protected:
// Preprocessor event handlers called by Clang.
void MacroExpands(const clang::Token& macro_use_token,
const clang::MacroDefinition& definition,
clang::SourceRange range,
const clang::MacroArgs* args) override;
void MacroDefined(const clang::Token& id,
const clang::MacroDirective* directive) override;
// Not needed for iwyu:
// virtual void MacroUndefined(const clang::Token&, const clang::MacroInfo*);
void Ifdef(clang::SourceLocation loc, const clang::Token& id,
const clang::MacroDefinition& definition) override;
void Ifndef(clang::SourceLocation loc, const clang::Token& id,
const clang::MacroDefinition& definition) override;
void Defined(const clang::Token& id,
const clang::MacroDefinition& definition,
clang::SourceRange range) override;
// Not needed for iwyu:
// virtual void If();
// virtual void Elif();
// virtual void Else();
// virtual void Endif();
void InclusionDirective(clang::SourceLocation hash_loc,
const clang::Token& include_token,
llvm::StringRef filename,
bool is_angled,
clang::CharSourceRange filename_range,
clang::OptionalFileEntryRef file,
llvm::StringRef search_path,
llvm::StringRef relative_path,
const clang::Module* imported,
clang::SrcMgr::CharacteristicKind file_type) override;
void FileChanged(clang::SourceLocation loc, FileChangeReason reason,
clang::SrcMgr::CharacteristicKind file_type,
clang::FileID exiting_from_id) override;
void FileSkipped(const clang::FileEntryRef& file, const clang::Token &filename,
clang::SrcMgr::CharacteristicKind file_type) override;
// FileChanged is actually a multi-plexer for 4 different callbacks.
void FileChanged_EnterFile(clang::SourceLocation file_beginning);
void FileChanged_ExitToFile(clang::SourceLocation include_loc,
const clang::FileEntry* exiting_from);
void FileChanged_RenameFile(clang::SourceLocation new_file);
void FileChanged_SystemHeaderPragma(clang::SourceLocation loc);
// CommentHandler callback.
// Clang doc: The handler shall return true if it has pushed any
// tokens to be read using e.g. EnterToken or EnterTokenStream.
bool HandleComment(clang::Preprocessor& pp,
clang::SourceRange comment_range) override;
private:
// Returns true if includee is considered part of the main
// compilation unit. We always generate warnings for violations in
// files are part of the main compilation unit.
bool BelongsToMainCompilationUnit(const clang::FileEntry* includer,
const clang::FileEntry* includee) const;
// Creates a new iwyu_file_info_map_[file_entry] if it doesn't exist,
// or a noop otherwise. quoted_include_name is used to create the
// new entry if necessary.
void InsertIntoFileInfoMap(const clang::FileEntry* file,
const string& quoted_include_name);
// Helper function that returns iwyu_file_info_map_[file_entry] if
// it already exists, or creates a new one and returns it otherwise.
// If it creates a new one, it generates the quoted_include_name
// from the file-path for 'file'.
// TODO(csilvers): see if, in practice, all uses in here are just 'get's.
IwyuFileInfo* GetFromFileInfoMap(const clang::FileEntry* file);
// Helper for AddDirectInclude. Checks if we should protect the
// #include from iwyu removal.
void MaybeProtectInclude(clang::SourceLocation includer_loc,
const clang::FileEntry* includee,
const string& include_name_as_written);
// Called whenever an #include is seen in the preprocessor output.
void AddDirectInclude(clang::SourceLocation includer_loc,
const clang::FileEntry* includee,
const string& include_name_as_written);
// Report a "begin_exports"/"end_exports" pragma pair.
// begin_line is first line, end_line is just after the last line.
void AddExportedRange(const clang::FileEntry* file,
int begin_line, int end_line);
// Determine if the comment is a pragma, and if so, process it.
void HandlePragmaComment(clang::SourceRange comment_range);
// Process @headername directives in a file.
void ProcessHeadernameDirectivesInFile(clang::SourceLocation file_beginning);
// Checks whether it's OK to use the given macro defined in file defined_in.
void ReportMacroUse(const string& name,
clang::SourceLocation usage_location,
clang::SourceLocation dfn_location);
// As above, but get the definition location from macros_definition_loc_.
void FindAndReportMacroUse(const string& name, clang::SourceLocation loc);
// Final-processing routines done after all header files have been read.
void DoFinalMacroChecks();
// Helper for PopulateIntendsToProvideMap().
void AddAllIncludesAsFileEntries(const clang::FileEntry* includer,
set<const clang::FileEntry*>* retval) const;
void PopulateIntendsToProvideMap();
void PopulateTransitiveIncludeMap();
// Return true if at the current point in the parse of the given file,
// there is a pending "begin_exports" pragma.
bool HasOpenBeginExports(const clang::FileEntry* file) const;
// Return true if at the current point in the parse of the given files,
// there is a pending "begin_keep" pragma.
bool HasOpenBeginKeep(const clang::FileEntry* file) const;
// The C++ source file passed in as an argument to the compiler (as
// opposed to other files seen via #includes).
const clang::FileEntry* main_file_;
// All files that we should report iwyu violations in. It defaults
// to the "main compilation unit" (e.g. foo.cc, foo.h, foo-inl.h):
// main_file_ and its associated .h and -inl.h files, if they exist.
// But users can add to it via the --check_also flag.
set<const clang::FileEntry*> files_to_report_iwyu_violations_for_;
// These store macros seen, as we see them, and also macros that are
// called from other macros. We use this to do limited iwyu-testing
// on macro tokens (we'd love to test macro bodies more completely
// -- like we do template bodies -- but macros don't give us enough
// context to know how to interpret the tokens we see, in general).
map<string, clang::SourceLocation> macros_definition_loc_; // key: macro name
// This should logically be a set, but set<> needs Token::operator<
// which we don't have. Luckily, a vector works just as well.
vector<clang::Token> macros_called_from_macros_;
// This maps from the include-name as written in the program
// (including <>'s or ""'s) to the FileEntry we loaded for that
// #include.
map<string, const clang::FileEntry*> include_to_fileentry_map_;
map<const clang::FileEntry*, IwyuFileInfo> iwyu_file_info_map_;
// How many #include lines we've encountered from the given file.
map<const clang::FileEntry*, int> num_includes_seen_;
// Maps from a FileEntry* to all files that this file "intends" to
// provide the symbols from. For now, we say a file intentionally
// provides a symbol if it defines it, or if any file it directly
// #includes defines it. However, if the header is a private header
// -- as determined by the fact it's in the private->public header
// map -- we relax the second requirement to allow any file directly
// or indirectly included by the public file. This isn't perfect,
// but is as close as we can be to matching the intent of the author
// of the public/private system.
map<const clang::FileEntry*,
set<const clang::FileEntry*>> intends_to_provide_map_;
// Maps from a FileEntry* to all the files that this file includes,
// either directly or indirectly.
map<const clang::FileEntry*,
set<const clang::FileEntry*>> transitive_include_map_;
// Maps from a FileEntry* to the quoted names of files that its file
// is directed *not* to include via the "no_include" pragma.
map<const clang::FileEntry*, set<string>> no_include_map_;
// Maps from a FileEntry* to the qualified names of symbols that its
// file is directed *not* to forward-declare via the
// "no_forward_declare" pragma.
map<const clang::FileEntry*, set<string>> no_forward_declare_map_;
const IwyuFileInfo empty_file_info_;
// For processing pragmas. It is the current stack of open
// "begin_exports". There should be at most one item in this stack
// per file in the current inclusion chain..
stack<clang::SourceLocation> begin_exports_location_stack_;
// For processing pragmas. It is the current stack of open "begin_keep"s.
// There should be at most one item in this stack per file in the current
// inclusion chain.
stack<clang::SourceLocation> begin_keep_location_stack_;
// For processing forward decls. It is a multimap containing the bounds of
// every keep range.
multimap<const clang::FileEntry*, clang::SourceRange> keep_location_ranges_;
// For processing forward decls. It is a multimap containing the bounds of
// every export range.
multimap<const clang::FileEntry*, clang::SourceRange> export_location_ranges_;
// For processing associated pragma. It is the current open
// "associated" pragma.
clang::SourceLocation associated_pragma_location_;
// Filename spelling location in the last encountered inclusion directive.
// Should be used only in FileChanged_EnterFile, FileSkipped when
// corresponding callback is caused by inclusion directive. Don't use in
// other places because it is unclear which inclusion directive filename
// location corresponds to.
clang::SourceLocation include_filename_loc_;
};
} // namespace include_what_you_use
#endif // INCLUDE_WHAT_YOU_USE_IWYU_PREPROCESSOR_H_