reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    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
//===--- Headers.h - Include headers -----------------------------*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEADERS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEADERS_H

#include "Path.h"
#include "Protocol.h"
#include "SourceCode.h"
#include "index/Symbol.h"
#include "clang/Format/Format.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Tooling/Inclusions/HeaderIncludes.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/VirtualFileSystem.h"

namespace clang {
namespace clangd {

/// Returns true if \p Include is literal include like "path" or <path>.
bool isLiteralInclude(llvm::StringRef Include);

/// Represents a header file to be #include'd.
struct HeaderFile {
  std::string File;
  /// If this is true, `File` is a literal string quoted with <> or "" that
  /// can be #included directly; otherwise, `File` is an absolute file path.
  bool Verbatim;

  bool valid() const;
};

/// Creates a `HeaderFile` from \p Header which can be either a URI or a literal
/// include.
llvm::Expected<HeaderFile> toHeaderFile(llvm::StringRef Header,
                                        llvm::StringRef HintPath);

// Returns include headers for \p Sym sorted by popularity. If two headers are
// equally popular, prefer the shorter one.
llvm::SmallVector<llvm::StringRef, 1> getRankedIncludes(const Symbol &Sym);

// An #include directive that we found in the main file.
struct Inclusion {
  Range R;             // Inclusion range.
  std::string Written; // Inclusion name as written e.g. <vector>.
  Path Resolved;       // Resolved path of included file. Empty if not resolved.
  unsigned HashOffset = 0; // Byte offset from start of file to #.
  SrcMgr::CharacteristicKind FileKind = SrcMgr::C_User;
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Inclusion &);

// Contains information about one file in the build grpah and its direct
// dependencies. Doesn't own the strings it references (IncludeGraph is
// self-contained).
struct IncludeGraphNode {
  enum class SourceFlag : uint8_t {
    None = 0,
    // Whether current file is a main file rather than a header.
    IsTU = 1 << 0,
    // Whether current file had any uncompilable errors during indexing.
    HadErrors = 1 << 1,
  };

  SourceFlag Flags = SourceFlag::None;
  llvm::StringRef URI;
  FileDigest Digest{{0}};
  std::vector<llvm::StringRef> DirectIncludes;
};
// FileURI and FileInclusions are references to keys of the map containing
// them.
// Important: The graph generated by those callbacks might contain cycles, self
// edges and multi edges.
using IncludeGraph = llvm::StringMap<IncludeGraphNode>;

inline IncludeGraphNode::SourceFlag operator|(IncludeGraphNode::SourceFlag A,
                                              IncludeGraphNode::SourceFlag B) {
  return static_cast<IncludeGraphNode::SourceFlag>(static_cast<uint8_t>(A) |
                                                   static_cast<uint8_t>(B));
}

inline bool operator&(IncludeGraphNode::SourceFlag A,
                      IncludeGraphNode::SourceFlag B) {
  return static_cast<uint8_t>(A) & static_cast<uint8_t>(B);
}

inline IncludeGraphNode::SourceFlag &
operator|=(IncludeGraphNode::SourceFlag &A, IncludeGraphNode::SourceFlag B) {
  return A = A | B;
}

// Information captured about the inclusion graph in a translation unit.
// This includes detailed information about the direct #includes, and summary
// information about all transitive includes.
//
// It should be built incrementally with collectIncludeStructureCallback().
// When we build the preamble, we capture and store its include structure along
// with the preamble data. When we use the preamble, we can copy its
// IncludeStructure and use another collectIncludeStructureCallback() to fill
// in any non-preamble inclusions.
class IncludeStructure {
public:
  std::vector<Inclusion> MainFileIncludes;

  // Return all transitively reachable files, and their minimum include depth.
  // All transitive includes (absolute paths), with their minimum include depth.
  // Root --> 0, #included file --> 1, etc.
  // Root is clang's name for a file, which may not be absolute.
  // Usually it should be SM.getFileEntryForID(SM.getMainFileID())->getName().
  llvm::StringMap<unsigned> includeDepth(llvm::StringRef Root) const;

  // This updates IncludeDepth(), but not MainFileIncludes.
  void recordInclude(llvm::StringRef IncludingName,
                     llvm::StringRef IncludedName,
                     llvm::StringRef IncludedRealName);

private:
  // Identifying files in a way that persists from preamble build to subsequent
  // builds is surprisingly hard. FileID is unavailable in InclusionDirective(),
  // and RealPathName and UniqueID are not preserved in the preamble.
  // We use the FileEntry::Name, which is stable, interned into a "file index".
  // The paths we want to expose are the RealPathName, so store those too.
  std::vector<std::string> RealPathNames; // In file index order.
  unsigned fileIndex(llvm::StringRef Name);
  llvm::StringMap<unsigned> NameToIndex; // Values are file indexes.
  // Maps a file's index to that of the files it includes.
  llvm::DenseMap<unsigned, SmallVector<unsigned, 8>> IncludeChildren;
};

/// Returns a PPCallback that visits all inclusions in the main file.
std::unique_ptr<PPCallbacks>
collectIncludeStructureCallback(const SourceManager &SM, IncludeStructure *Out);

// Calculates insertion edit for including a new header in a file.
class IncludeInserter {
public:
  // If \p HeaderSearchInfo is nullptr (e.g. when compile command is
  // infeasible), this will only try to insert verbatim headers, and
  // include path of non-verbatim header will not be shortened.
  IncludeInserter(StringRef FileName, StringRef Code,
                  const format::FormatStyle &Style, StringRef BuildDir,
                  HeaderSearch *HeaderSearchInfo)
      : FileName(FileName), Code(Code), BuildDir(BuildDir),
        HeaderSearchInfo(HeaderSearchInfo),
        Inserter(FileName, Code, Style.IncludeStyle) {}

  void addExisting(const Inclusion &Inc);

  /// Checks whether to add an #include of the header into \p File.
  /// An #include will not be added if:
  ///   - Either \p DeclaringHeader or \p InsertedHeader is already (directly)
  ///   in \p Inclusions (including those included via different paths).
  ///   - \p DeclaringHeader or \p InsertedHeader is the same as \p File.
  ///
  /// \param DeclaringHeader is path of the original header corresponding to \p
  /// InsertedHeader e.g. the header that declares a symbol.
  /// \param InsertedHeader The preferred header to be inserted. This could be
  /// the same as DeclaringHeader but must be provided.
  bool shouldInsertInclude(PathRef DeclaringHeader,
                           const HeaderFile &InsertedHeader) const;

  /// Determines the preferred way to #include a file, taking into account the
  /// search path. Usually this will prefer a shorter representation like
  /// 'Foo/Bar.h' over a longer one like 'Baz/include/Foo/Bar.h'.
  ///
  /// \param InsertedHeader The preferred header to be inserted.
  ///
  /// \param IncludingFile is the absolute path of the file that InsertedHeader
  /// will be inserted.
  ///
  /// \return A quoted "path" or <path> to be included, or None if it couldn't
  /// be shortened.
  llvm::Optional<std::string>
  calculateIncludePath(const HeaderFile &InsertedHeader,
                       llvm::StringRef IncludingFile) const;

  /// Calculates an edit that inserts \p VerbatimHeader into code. If the header
  /// is already included, this returns None.
  llvm::Optional<TextEdit> insert(llvm::StringRef VerbatimHeader) const;

private:
  StringRef FileName;
  StringRef Code;
  StringRef BuildDir;
  HeaderSearch *HeaderSearchInfo = nullptr;
  llvm::StringSet<> IncludedHeaders; // Both written and resolved.
  tooling::HeaderIncludes Inserter;  // Computers insertion replacement.
};

} // namespace clangd
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEADERS_H