//===--- 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 // for map #include // for set #include // for string, operator< #include // 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 ), but we don't have // 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& 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 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; // - bool is_desired() const { return is_desired_; } bool is_present() const { return is_present_; } const map& 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 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, '' 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: '' 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& 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& direct_includes() const { return direct_includes_; } const set& desired_includes() const { CHECK_(desired_includes_have_been_calculated_ && "Must calculate desired includes before calling desired_includes()"); return desired_includes_; } set AssociatedQuotedIncludes() const { set associated_quoted_includes; for (const IwyuFileInfo* associated : associated_headers_) associated_quoted_includes.insert(associated->quoted_file_); return associated_quoted_includes; } set AssociatedFileEntries() const { set associated_file_entries; for (const IwyuFileInfo* associated : associated_headers_) associated_file_entries.insert(associated->file_); return associated_file_entries; } set AssociatedDesiredIncludes() const { set 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* uses); // Uses uses to emit warning messages (at high enough verbosity). // Returns the number of warning messages found. int EmitWarningMessages(const vector& 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 associated_headers_; // Holds all the uses that are reported. vector symbol_uses_; // Holds all the lines (#include and fwd-declare) that are reported. vector 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 using_decl_referenced_; // We also hold the line information in a few other data structures, // for ease of references. set direct_includes_; // key is the quoted include, eg '' set direct_includes_as_fileentries_; set direct_forward_declares_; // Holds files forced to be kept. For example, files included with the // "IWYU pragma: keep" comment and x-macros. set kept_includes_; // Holds files using macros defined in this file. set macro_users_; // What we will recommend the #includes to be. set 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_