449 lines
16 KiB
C++
449 lines
16 KiB
C++
//===--- 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;
|
|
} // 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::UsingDecl* 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::UsingDecl*, 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_
|