aboutsummaryrefslogtreecommitdiff
blob: 6f0eb807dcce988af174bded402ee3add668065f (plain)
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
//===--- Headers.h - Include headers -----------------------------*- C++-*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#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 "clang/Format/Format.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Tooling/Inclusions/HeaderIncludes.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;
};

// 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 {
  // True if current file is a main file rather than a header.
  bool IsTU = false;
  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>;

// 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 preseved 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:
  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 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(const HeaderFile &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 DeclaringHeader is 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.
  ///
  /// \return A quoted "path" or <path> to be included.
  std::string calculateIncludePath(const HeaderFile &DeclaringHeader,
                                   const HeaderFile &InsertedHeader) 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;
  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