//===--- 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_
