//===--- iwyu_include_picker.h - map to canonical #includes for iwyu ------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// The include-picker provides a list of candidate #include-lines
// that iwyu can suggest in order to include a particular symbol
// or file.
//
// It seems like the 'file' case would be easy ("to include
// /usr/include/math.h, say '#include <math.h>"), but it's
// not because many header files are private, and should not
// be included by users directly.  A private header will have
// one or (occassionally) more public headers that it maps to.
// The include-picker keeps track of these mappings.
//
// It's also possible for a public file to have an include-picker
// mapping.  This means: "it's ok to #include this file directly, but
// you can also get the contents of this file by #including this other
// file as well."  One example is that <ostream> maps to both
// <ostream> and <iostream>.  Other parts of iwyu can decide which
// #include to suggest based on its own heuristics (whether the file
// already needs to #include <iostream> for some other reason, for
// instance).
//
// Some of these mappings are hard-coded, based on my own examination
// of gcc headers on ubuntu.  Some mappings are determined at runtime,
// based on #pragmas or other writeup in the source files themselves.
//
// Mapping a symbol to a file has the same issues.  In most cases, a
// symbol maps to the file that defines it, and iwyu_include_picker
// has nothing useful to say.  But some symbols -- which we hard-code
// -- can be provided by several files.  NULL is a canonical example
// of this.
//
// The include-picker also provides some helper functions for
// converting from file-paths to #include paths, including, routines to
// normalize a file-path to get rid of /usr/include/ prefixes.

#ifndef INCLUDE_WHAT_YOU_USE_IWYU_INCLUDE_PICKER_H_
#define INCLUDE_WHAT_YOU_USE_IWYU_INCLUDE_PICKER_H_

#include <map>                          // for map, map<>::value_compare
#include <set>                          // for set
#include <string>                       // for string
#include <utility>                      // for pair
#include <vector>                       // for vector

namespace clang {
class FileEntry;
}  // namespace clang

namespace include_what_you_use {

using std::map;
using std::pair;
using std::set;
using std::string;

using std::vector;

struct IncludeMapEntry;

enum IncludeVisibility { kUnusedVisibility, kPublic, kPrivate };

// When a symbol or file is mapped to an include, that include is represented
// by this struct.  It always has a quoted_include and may also have a path
// (depending on its origin).
struct MappedInclude {
  explicit MappedInclude(const string& quoted_include,
                         const string& path = {});

  string quoted_include;
  string path;

  bool HasAbsoluteQuotedInclude() const;
};

class IncludePicker {
 public:
  // The keys are either symbol names or quoted includes, and the values are
  // lists of candidate public headers to include for symbol or quoted include.
  typedef map<string, vector<MappedInclude>> IncludeMap;

  // Used to track visibility as specified either in mapping files or via
  // pragmas.  The keys are quoted includes or paths.  The values are the
  // visibility of the respective files.
  typedef map<string, IncludeVisibility> VisibilityMap;

  explicit IncludePicker(bool no_default_mappings);

  // ----- Routines to dynamically modify the include-picker

  // Call this for every #include seen during iwyu analysis.  The
  // include-picker can use this data to better suggest #includes,
  // perhaps.
  void AddDirectInclude(const string& includer_filepath,
                        const string& includee_filepath,
                        const string& quoted_include_as_written);

  // Add this to say "map_to re-exports everything in file map_from".
  // map_from should be a quoted include.
  void AddMapping(const string& map_from, const MappedInclude& map_to);

  // Indicate that the given quoted include should be considered
  // a "private" include.  If possible, we use the include-picker
  // mappings to map such includes to public (not-private) includes.
  void MarkIncludeAsPrivate(const string& quoted_include);

  // Indicate that the given path should be considered
  // a "private" include.  If possible, we use the include-picker
  // mappings to map such includes to public (not-private) includes.
  void MarkPathAsPrivate(const string& path);

  // Add this to say that "any file whose name matches the
  // friend_regex is allowed to include includee_filepath".  The regex
  // uses the POSIX Entended Regular Expression syntax and should
  // match a quoted-include (starting and ending with "" or <>).
  void AddFriendRegex(const string& includee_filepath,
                      const string& quoted_friend_regex);

  // Call this after iwyu preprocessing is done.  No more calls to
  // AddDirectInclude() or AddMapping() are allowed after this.
  void FinalizeAddedIncludes();

  // ----- Include-picking API

  // Returns the set of all public header files that 'provide' the
  // given symbol.  For instance, NULL can map to stddef.h, stdlib.h,
  // etc.  Most symbols don't have pre-defined headers they map to,
  // and we return the empty vector in that case.  Ordering is
  // important (which is why we return a vector, not a set): all else
  // being equal, the first element of the vector is the "best" (or
  // most standard) header for the symbol.
  vector<MappedInclude> GetCandidateHeadersForSymbol(
      const string& symbol) const;

  // As above, but given a specific including header it is possible to convert
  // mapped includes to quoted include strings (because we can for example know
  // the correct relative path for ""-style includes).
  vector<string> GetCandidateHeadersForSymbolUsedFrom(
      const string& symbol, const string& including_filepath) const;

  // Returns the set of all public header files that a given header
  // file -- specified as a full path -- would map to, as a set of
  // MappedIncludes.  If the include-picker has
  // no mapping information for this file, the return vector has just
  // the input file (now include-quoted).  Ordering is important
  // (which is why we return a vector, not a set): all else being
  // equal, the first element of the vector is the "best" (or most
  // standard) header for the input header.
  vector<MappedInclude> GetCandidateHeadersForFilepath(
      const string& filepath, const string& including_filepath = "") const;

  // This allows for special-casing of GetCandidateHeadersForFilepath
  // -- it's the same, but you give it the filepath that's doing the
  // #including.  This lets us give a different answer for different
  // call-sites.  For instance, "foo/internal/bar.h" is a fine
  // candidate header when #included from "foo/internal/baz.h", but
  // not when #included from "qux/quux.h".  In the common case there's
  // no special-casing, and this falls back on
  // GetCandidateHeadersForFilepath().
  // Furthermore, knowing the including file allows use to convert each
  // MappedInclude in the result to a simple string (quoted include).
  vector<string> GetCandidateHeadersForFilepathIncludedFrom(
      const string& included_filepath, const string& including_filepath) const;

  // Returns true if there is a mapping (possibly indirect) from
  // map_from to map_to.  This means that to_file 're-exports' all the
  // symbols from from_file.  Both map_from_filepath and
  // map_to_filepath should be full file-paths.
  bool HasMapping(const string& map_from_filepath,
                  const string& map_to_filepath) const;

  bool IsPublic(const clang::FileEntry* file) const;

  // Parses a YAML/JSON file containing mapping directives of various types.
  void AddMappingsFromFile(const string& filename);

 private:
  // Private implementation of mapping file parser, which takes
  // mapping file search path to allow recursion that builds up
  // search path incrementally.
  void AddMappingsFromFile(const string& filename,
                           const vector<string>& search_path);

  // Adds all hard-coded default mappings.
  void AddDefaultMappings();

  // Adds a mapping from a one header to another, typically
  // from a private to a public quoted include.
  void AddIncludeMapping(
      const string& map_from, IncludeVisibility from_visibility,
      const MappedInclude& map_to, IncludeVisibility to_visibility);

  // Adds a mapping from a a symbol to a quoted include. We use this to
  // maintain mappings of documented types, e.g.
  //  For std::map<>, include <map>.
  void AddSymbolMapping(
      const string& map_from, const MappedInclude& map_to,
      IncludeVisibility to_visibility);

  // Adds mappings from sized arrays of IncludeMapEntry.
  void AddIncludeMappings(const IncludeMapEntry* entries, size_t count);
  void AddSymbolMappings(const IncludeMapEntry* entries, size_t count);

  void AddPublicIncludes(const char** includes, size_t count);

  // Expands the regex keys in filepath_include_map_ and
  // friend_to_headers_map_ by matching them against all source files
  // seen by iwyu.
  void ExpandRegexes();

  // Adds an entry to the given VisibilityMap, with error checking.
  void MarkVisibility(VisibilityMap* map, const string& key,
                      IncludeVisibility visibility);

  // Parse visibility from a string. Returns kUnusedVisibility if
  // string is not recognized.
  IncludeVisibility ParseVisibility(const string& visibility) const;

  // Return the visibility of a given mapped include if known, else
  // kUnusedVisibility.
  IncludeVisibility GetVisibility(
      const MappedInclude&,
      IncludeVisibility default_value = kUnusedVisibility) const;

  // For the given key, return the vector of values associated with
  // that key, or an empty vector if the key does not exist in the
  // map, filtering out private files.
  vector<MappedInclude> GetPublicValues(const IncludeMap& m,
                                        const string& key) const;

  // Given an includer-pathname and includee-pathname, return the
  // quoted-include of the includee, as written in the includer, or
  // "" if it's not found for some reason.
  string MaybeGetIncludeNameAsWritten(const string& includer_filepath,
                                      const string& includee_filepath) const;

  // Given a collection of MappedIncludes, and a path that might include them,
  // choose the best quoted include form for each MappedInclude.
  vector<string> BestQuotedIncludesForIncluder(
      const vector<MappedInclude>&, const string& including_filepath) const;

  // From symbols to includes.
  IncludeMap symbol_include_map_;

  // From quoted filepath patterns to includes, where a pattern can be
  // either a quoted filepath (e.g. "foo/bar.h" or <a/b.h>) or @
  // followed by a regular expression for matching a quoted filepath
  // (e.g. @"foo/.*").  If key-value pair (pattern, headers) is in
  // this map, it means that any header in 'headers' can be used to
  // get symbols exported by a header matching 'pattern'.
  IncludeMap filepath_include_map_;

  // A map of all quoted-includes to whether they're public or private.
  // Files whose visibility cannot be determined by this map nor the one
  // below are assumed public.
  VisibilityMap include_visibility_map_;

  // A map of paths to whether they're public or private.
  // Files whose visibility cannot be determined by this map nor the one
  // above are assumed public.
  // The include_visibility_map_ takes priority over this one.
  VisibilityMap path_visibility_map_;

  // All the includes we've seen so far, to help with globbing and
  // other dynamic mapping.  For each file, we list who #includes it.
  map<string, set<string>> quoted_includes_to_quoted_includers_;

  // Given the filepaths of an includer and includee, give the
  // include-as-written (including <>'s or ""'s) that the includer
  // used to refer to the includee.  We use this to return includes as
  // they were written in the source, when possible.
  map<pair<string, string>, string>
      includer_and_includee_to_include_as_written_;

  // Maps from a quoted filepath pattern to the set of files that used
  // a pragma declaring it as a friend.  That is, if foo/bar/x.h has a
  // line "// IWYU pragma: friend foo/bar/.*" then "x.h" will be a
  // member of friend_to_headers_map_["@\"foo/bar/.*\""]. In a
  // postprocessing step, files friend_to_headers_map_ will have
  // regular expressions expanded, e.g. if foo/bar/x.cc is processed,
  // friend_to_headers_map_["foo/bar/x.cc"] will be augmented with the
  // contents of friend_to_headers_map_["@\"foo/bar/.*\""].
  map<string, set<string>> friend_to_headers_map_;

  // Make sure we don't do any non-const operations after finalizing.
  bool has_called_finalize_added_include_lines_;
};  // class IncludePicker

}  // namespace include_what_you_use

#endif  // INCLUDE_WHAT_YOU_USE_IWYU_INCLUDE_PICKER_H_
