forked from include-what-you-use/include-what-you-use
-
Notifications
You must be signed in to change notification settings - Fork 4
/
iwyu_output.h
401 lines (330 loc) · 16.4 KB
/
iwyu_output.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
390
391
392
393
394
395
396
397
398
399
400
401
//===--- iwyu_output.h - output-emitting code for include-what-you-use ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This file contains routines to deal with all output emitted by
// iwyu plug-in. This includes functions to sanitize include-files
// (though most of the underlying logic is in iwyu_sanitize_filepath),
// to sanitize symbol names, to emit desired include-lines properly,
// etc.
#ifndef INCLUDE_WHAT_YOU_USE_IWYU_OUTPUT_H_
#define INCLUDE_WHAT_YOU_USE_IWYU_OUTPUT_H_
#include <map> // for map
#include <set> // for set
#include <string> // for string, operator<
#include <vector> // for vector
#include "iwyu_port.h" // for CHECK_
#include "iwyu_stl_util.h"
#include "iwyu_use_flags.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/SourceLocation.h"
namespace clang {
class FileEntry;
class UsingDecl;
class BaseUsingDecl;
} // namespace clang
namespace include_what_you_use {
using std::map;
using std::set;
using std::string;
using std::vector;
class IwyuPreprocessorInfo;
// This data structure holds information about a single use. Not all
// fields will be filled for all uses.
class OneUse {
public:
enum UseKind { kFullUse, kForwardDeclareUse };
OneUse(const clang::NamedDecl* decl,
clang::SourceLocation use_loc,
clang::SourceLocation decl_loc,
UseKind use_kind,
UseFlags flags,
const char* comment);
// Both dfn_file and dfn_filepath are specified to allow to create OneUse
// with dfn_filepath and without dfn_file. For example, in
// IwyuBaseAstVisitor::VisitCXXNewExpr we make a guess that placement
// operator new is called (which is defined in <new>), but we don't have
// <new> FileEntry.
OneUse(const string& symbol_name,
const clang::FileEntry* dfn_file,
const string& dfn_filepath,
clang::SourceLocation use_loc);
const string& symbol_name() const { return symbol_name_; }
const string& short_symbol_name() const { return short_symbol_name_; }
const clang::NamedDecl* decl() const { return decl_; }
const clang::FileEntry* decl_file() const { return decl_file_; }
const string& decl_filepath() const { return decl_filepath_; }
clang::SourceLocation use_loc() const { return use_loc_; }
clang::SourceLocation decl_loc() const { return decl_loc_; }
bool is_full_use() const { return use_kind_ == kFullUse; }
UseFlags flags() const { return use_flags_; }
const string& comment() const { return comment_; }
bool ignore_use() const { return ignore_use_; }
bool is_iwyu_violation() const { return is_iwyu_violation_; }
bool has_suggested_header() const { return !suggested_header_.empty(); }
const string& suggested_header() const {
CHECK_(has_suggested_header() && "Must assign suggested_header first");
CHECK_(!ignore_use() && "Ignored uses have no suggested header");
return suggested_header_;
}
void reset_decl(const clang::NamedDecl* decl);
void set_full_use() { use_kind_ = kFullUse; }
void set_forward_declare_use() { use_kind_ = kForwardDeclareUse; }
void set_ignore_use() { ignore_use_ = true; }
void set_is_iwyu_violation() { is_iwyu_violation_ = true; }
void set_suggested_header(const string& fh) { suggested_header_ = fh; }
string PrintableUseLoc() const;
const vector<string>& public_headers(); // not const because we fill lazily
bool PublicHeadersContain(const string& elt);
bool NeedsSuggestedHeader() const; // not true for fwd-declare uses, e.g.
int UseLinenum() const;
private:
void SetPublicHeaders(); // sets based on decl_filepath_
string symbol_name_; // the symbol being used
string short_symbol_name_; // 'short' form of the symbol being used
const clang::NamedDecl* decl_; // decl of the symbol, if we know it
clang::SourceLocation decl_loc_; // where the decl is attributed to live
const clang::FileEntry* decl_file_; // file entry where the symbol lives
string decl_filepath_; // filepath where the symbol lives
clang::SourceLocation use_loc_; // where the symbol is used from
UseKind use_kind_; // kFullUse or kForwardDeclareUse
UseFlags use_flags_; // flags describing features of the use
string comment_; // If not empty, append to clang warning msg
vector<string> public_headers_; // header to #include if dfn hdr is private
string suggested_header_; // header that allows us to satisfy use
bool ignore_use_; // set to true if use is discarded
bool is_iwyu_violation_; // set to false when we figure out it's not
};
class OneIncludeOrForwardDeclareLine {
public:
explicit OneIncludeOrForwardDeclareLine(const clang::NamedDecl* fwd_decl);
OneIncludeOrForwardDeclareLine(const clang::FileEntry* included_file,
const string& quoted_include, int linenum);
const string& line() const { return line_; }
bool IsIncludeLine() const; // vs forward-declare line
string LineNumberString() const; // <startline>-<endline>
bool is_desired() const { return is_desired_; }
bool is_present() const { return is_present_; }
const map<string, int>& symbol_counts() const { return symbol_counts_; }
string quoted_include() const {
CHECK_(IsIncludeLine() && "Must call quoted_include() on include lines");
CHECK_(!fwd_decl_ && "quoted_include and fwd_decl are mutually exclusive");
return quoted_include_;
}
const clang::FileEntry* included_file() const {
CHECK_(IsIncludeLine() && "Must call included_file() on include lines");
CHECK_(!fwd_decl_ && "included_file and fwd_decl are mutually exclusive");
return included_file_;
}
const clang::NamedDecl* fwd_decl() const {
CHECK_(!IsIncludeLine() && "Must call fwd_decl() on forward-declare lines");
CHECK_(quoted_include_.empty() && !included_file_ &&
"quoted_include and fwd_decl don't mix");
return fwd_decl_;
}
bool matches(const string& quoted_include) const {
return IsIncludeLine() && (quoted_include_ == quoted_include);
}
bool matches(const clang::NamedDecl* decl) const {
return !IsIncludeLine() && (fwd_decl_ == decl);
}
void set_present() { is_present_ = true; }
void set_desired() { is_desired_ = true; }
void clear_desired() { is_desired_ = false; }
void clear_line_numbers() { start_linenum_ = end_linenum_ = -1; }
// Another symbol we're using that's defined in this file.
void AddSymbolUse(const string& symbol_name);
bool HasSymbolUse(const string& symbol_name) const;
bool LineNumbersMatch(const OneIncludeOrForwardDeclareLine& that) const {
return (this->start_linenum_ == that.start_linenum_ &&
this->end_linenum_ == that.end_linenum_);
}
private:
string line_; // '#include XXX' or 'class YYY;'
int start_linenum_;
int end_linenum_;
bool is_desired_; // IWYU will recommend this line
bool is_present_; // line was present before the IWYU run
map<string, int> symbol_counts_; // how many times we referenced each symbol
// Only either two following members are set for includes
string quoted_include_; // quoted file name we're including
const clang::FileEntry* included_file_; // the file we're including
// ...or this member is set for the fwd-decl we're emitting.
const clang::NamedDecl* fwd_decl_;
};
// This class holds IWYU information about a single file (FileEntry)
// -- referred to, in the comments below, as "this file." The keys to
// most of these methods are all quoted header paths, which are the
// include names as they would occur in a source file, including <> or
// "". For instance, '<string>' or '"ads/test.h"'.
// TODO(csilvers): add unitests for this class.
class IwyuFileInfo {
public:
// TODO(csilvers): also take iwyufileinfos for 'associated' files (.h's).
// And a source-manager.
IwyuFileInfo(const clang::FileEntry* this_file,
const IwyuPreprocessorInfo* preprocessor_info,
const string& quoted_include_name);
bool is_prefix_header() const { return is_prefix_header_; }
void set_prefix_header() { is_prefix_header_ = true; }
bool is_pch_in_code() const { return is_pch_in_code_; }
void set_pch_in_code() { is_pch_in_code_ = true; }
// An 'associated' header is a header that this file #includes
// (possibly indirectly) that we should treat as being logically
// part of this file. In particular, when computing the direct
// includes of this file, we also include the direct includes of all
// associated headers. Examples: vector has bits/stl_vector.h as an
// associated header; foo.cc has foo.h and foo-inl.h as associated
// headers.
void AddAssociatedHeader(const IwyuFileInfo* other);
// Use these to register an iwyu declaration: either an #include,
// a forward-declaration or a using-declaration.
void AddInclude(const clang::FileEntry* includee,
const string& quoted_includee, int linenumber);
// definitely_keep_fwd_decl tells us that we should never suggest
// the fwd-decl be removed, even if we don't see any uses of it.
void AddForwardDeclare(const clang::NamedDecl* fwd_decl,
bool definitely_keep_fwd_decl);
void AddUsingDecl(const clang::UsingDecl* using_decl);
// Use these to register an iwyu 'use'. It's preferable to indicate
// an explicit type or decl being used, but if that's not available,
// a symbol-name is acceptable as well. There are two forms of each
// registration routine, one for when we need the full symbol info
// (via an #include), and one when forward-declaring is enough.
void ReportFullSymbolUse(clang::SourceLocation use_loc,
const clang::NamedDecl* decl,
UseFlags flags, const char* comment);
// This is used for symbols with a made up dfn_filepath. Currently it's used
// only for placement operator new in templates (see
// IwyuBaseAstVisitor::VisitCXXNewExpr).
void ReportFullSymbolUse(clang::SourceLocation use_loc,
const clang::FileEntry* dfn_file,
const string& symbol);
// TODO(dsturtevant): Can we determine in_cxx_method_body? Do we care?
// Called when using a macro in this file.
void ReportMacroUse(clang::SourceLocation use_loc,
clang::SourceLocation dfn_loc,
const string& symbol);
// Called when somebody uses a macro defined in this file.
void ReportDefinedMacroUse(const clang::FileEntry* used_in);
// We only allow forward-declaring of decls, not arbitrary symbols.
void ReportForwardDeclareUse(clang::SourceLocation use_loc,
const clang::NamedDecl* decl,
UseFlags flags, const char* comment);
// Called whenever a NamedDecl is accessed through a UsingDecl.
// ie: using std::swap; swap(a, b);
void ReportUsingDeclUse(clang::SourceLocation use_loc,
const clang::BaseUsingDecl* using_decl,
UseFlags flags, const char* comment);
// This is used when we see a // NOLINT comment, for instance. It says
// '#include this header file as-is, without any public-header mapping.'
// Input is the include-line as desired: '<string.h>' or '"ads/foo.h"'.
void ReportIncludeFileUse(const clang::FileEntry* included_file,
const string& quoted_include);
// This is used when we see a file we want to keep not due to symbol-use
// reasons. For example, it can be #included with an "IWYU pragma: keep"
// comment or it can be an x-macro.
void ReportKnownDesiredFile(const clang::FileEntry* included_file);
// This is used only in iwyu_preprocessor.cc. TODO(csilvers): revamp?
const set<const clang::FileEntry*>& direct_includes_as_fileentries() const {
return direct_includes_as_fileentries_;
}
// Called when all macros in the file are processed.
void HandlePreprocessingDone();
// Resolve and pending analysis that needs to occur between AST traversal
// and CalculateAndReportIwyuViolations.
void ResolvePendingAnalysis();
// The meat of iwyu: compare the actual includes and forward-declares
// against the symbol uses, and report which uses are iwyu violations.
// Reports violations on errs(), and returns the number of violations.
size_t CalculateAndReportIwyuViolations();
private:
const set<string>& direct_includes() const { return direct_includes_; }
const set<string>& desired_includes() const {
CHECK_(desired_includes_have_been_calculated_ &&
"Must calculate desired includes before calling desired_includes()");
return desired_includes_;
}
set<string> AssociatedQuotedIncludes() const {
set<string> associated_quoted_includes;
for (const IwyuFileInfo* associated : associated_headers_)
associated_quoted_includes.insert(associated->quoted_file_);
return associated_quoted_includes;
}
set<const clang::FileEntry*> AssociatedFileEntries() const {
set<const clang::FileEntry*> associated_file_entries;
for (const IwyuFileInfo* associated : associated_headers_)
associated_file_entries.insert(associated->file_);
return associated_file_entries;
}
set<string> AssociatedDesiredIncludes() const {
set<string> associated_desired_includes;
for (const IwyuFileInfo* associated : associated_headers_)
InsertAllInto(associated->desired_includes(),
&associated_desired_includes);
return associated_desired_includes;
}
// Populates uses with full data, including is_iwyu_violation_.
void CalculateIwyuViolations(vector<OneUse>* uses);
// Uses uses to emit warning messages (at high enough verbosity).
// Returns the number of warning messages found.
int EmitWarningMessages(const vector<OneUse>& uses);
// The constructor arguments. file_ is 'this file'.
const clang::FileEntry* file_;
const IwyuPreprocessorInfo* preprocessor_info_;
string quoted_file_;
// Prefix header means included from command line via -include option.
bool is_prefix_header_;
// PCH in code refers to an #include directive that acts as a marker for
// precompiled header inclusion. This pattern can be used with GCC and is
// standard practice on MSVC, whereas Clang forces PCHs to be listed as prefix
// headers.
bool is_pch_in_code_;
// associated_headers_ are the files 'associated' with this file: if
// this file is foo.cc, associated_headers_ are the IwyuFileInfo's for
// foo.h and foo-inl.h, if present.
set<const IwyuFileInfo*> associated_headers_;
// Holds all the uses that are reported.
vector<OneUse> symbol_uses_;
// Holds all the lines (#include and fwd-declare) that are reported.
vector<OneIncludeOrForwardDeclareLine> lines_;
// Maps all the using-decls that are reported to a bool indicating whether
// or not a the using decl has been referenced in this file.
map<const clang::BaseUsingDecl*, bool> using_decl_referenced_;
// We also hold the line information in a few other data structures,
// for ease of references.
set<string> direct_includes_; // key is the quoted include, eg '<set>'
set<const clang::FileEntry*> direct_includes_as_fileentries_;
set<const clang::NamedDecl*> direct_forward_declares_;
// Holds files forced to be kept. For example, files included with the
// "IWYU pragma: keep" comment and x-macros.
set<const clang::FileEntry*> kept_includes_;
// Holds files using macros defined in this file.
set<const clang::FileEntry*> macro_users_;
// What we will recommend the #includes to be.
set<string> desired_includes_;
bool desired_includes_have_been_calculated_;
};
// Helpers for testing.
namespace internal {
class FakeNamedDecl : public clang::NamedDecl {
public:
FakeNamedDecl(const string& kind_name, const string& qual_name,
const string& decl_filepath, int decl_linenum);
string kind_name() const { return kind_name_; }
string qual_name() const { return qual_name_; }
string decl_filepath() const { return decl_filepath_; }
int decl_linenum() const { return decl_linenum_; }
private:
string kind_name_;
string qual_name_;
string decl_filepath_;
int decl_linenum_;
};
} // namespace internal
} // namespace include_what_you_use
#endif // INCLUDE_WHAT_YOU_USE_IWYU_OUTPUT_H_