1 | //===--- Headers.h - Include headers -----------------------------*- C++-*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEADERS_H |
10 | #define |
11 | |
12 | #include "Protocol.h" |
13 | #include "SourceCode.h" |
14 | #include "index/Symbol.h" |
15 | #include "support/Path.h" |
16 | #include "clang/Basic/FileEntry.h" |
17 | #include "clang/Basic/TokenKinds.h" |
18 | #include "clang/Format/Format.h" |
19 | #include "clang/Frontend/CompilerInstance.h" |
20 | #include "clang/Lex/HeaderSearch.h" |
21 | #include "clang/Lex/Preprocessor.h" |
22 | #include "clang/Tooling/Inclusions/HeaderIncludes.h" |
23 | #include "clang/Tooling/Inclusions/StandardLibrary.h" |
24 | #include "llvm/ADT/ArrayRef.h" |
25 | #include "llvm/ADT/DenseSet.h" |
26 | #include "llvm/ADT/StringRef.h" |
27 | #include "llvm/ADT/StringSet.h" |
28 | #include "llvm/Support/Error.h" |
29 | #include "llvm/Support/FileSystem/UniqueID.h" |
30 | #include <optional> |
31 | #include <string> |
32 | |
33 | namespace clang { |
34 | namespace clangd { |
35 | |
36 | /// Returns true if \p Include is literal include like "path" or <path>. |
37 | bool isLiteralInclude(llvm::StringRef Include); |
38 | |
39 | /// Represents a header file to be #include'd. |
40 | struct { |
41 | std::string ; |
42 | /// If this is true, `File` is a literal string quoted with <> or "" that |
43 | /// can be #included directly; otherwise, `File` is an absolute file path. |
44 | bool ; |
45 | |
46 | bool () const; |
47 | }; |
48 | |
49 | /// A header and directives as stored in a Symbol. |
50 | struct SymbolInclude { |
51 | /// The header to include. This is either a URI or a verbatim include which is |
52 | /// quoted with <> or "". |
53 | llvm::StringRef ; |
54 | /// The include directive(s) that can be used, e.g. #import and/or #include. |
55 | Symbol::IncludeDirective Directive; |
56 | }; |
57 | |
58 | /// Creates a `HeaderFile` from \p Header which can be either a URI or a literal |
59 | /// include. |
60 | llvm::Expected<HeaderFile> (llvm::StringRef , |
61 | llvm::StringRef HintPath); |
62 | |
63 | // Returns include headers for \p Sym sorted by popularity. If two headers are |
64 | // equally popular, prefer the shorter one. |
65 | llvm::SmallVector<SymbolInclude, 1> getRankedIncludes(const Symbol &Sym); |
66 | |
67 | // An #include directive that we found in the main file. |
68 | struct Inclusion { |
69 | tok::PPKeywordKind Directive; // Directive used for inclusion, e.g. import |
70 | std::string Written; // Inclusion name as written e.g. <vector>. |
71 | Path Resolved; // Resolved path of included file. Empty if not resolved. |
72 | unsigned HashOffset = 0; // Byte offset from start of file to #. |
73 | int HashLine = 0; // Line number containing the directive, 0-indexed. |
74 | SrcMgr::CharacteristicKind FileKind = SrcMgr::C_User; |
75 | std::optional<unsigned> ; |
76 | }; |
77 | llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Inclusion &); |
78 | bool operator==(const Inclusion &LHS, const Inclusion &RHS); |
79 | |
80 | // Contains information about one file in the build graph and its direct |
81 | // dependencies. Doesn't own the strings it references (IncludeGraph is |
82 | // self-contained). |
83 | struct IncludeGraphNode { |
84 | enum class SourceFlag : uint8_t { |
85 | None = 0, |
86 | // Whether current file is a main file rather than a header. |
87 | IsTU = 1 << 0, |
88 | // Whether current file had any uncompilable errors during indexing. |
89 | HadErrors = 1 << 1, |
90 | }; |
91 | |
92 | SourceFlag Flags = SourceFlag::None; |
93 | llvm::StringRef URI; |
94 | FileDigest Digest{._M_elems: {0}}; |
95 | std::vector<llvm::StringRef> DirectIncludes; |
96 | }; |
97 | // FileURI and FileInclusions are references to keys of the map containing |
98 | // them. |
99 | // Important: The graph generated by those callbacks might contain cycles, self |
100 | // edges and multi edges. |
101 | using IncludeGraph = llvm::StringMap<IncludeGraphNode>; |
102 | |
103 | inline IncludeGraphNode::SourceFlag operator|(IncludeGraphNode::SourceFlag A, |
104 | IncludeGraphNode::SourceFlag B) { |
105 | return static_cast<IncludeGraphNode::SourceFlag>(static_cast<uint8_t>(A) | |
106 | static_cast<uint8_t>(B)); |
107 | } |
108 | |
109 | inline bool operator&(IncludeGraphNode::SourceFlag A, |
110 | IncludeGraphNode::SourceFlag B) { |
111 | return static_cast<uint8_t>(A) & static_cast<uint8_t>(B); |
112 | } |
113 | |
114 | inline IncludeGraphNode::SourceFlag & |
115 | operator|=(IncludeGraphNode::SourceFlag &A, IncludeGraphNode::SourceFlag B) { |
116 | return A = A | B; |
117 | } |
118 | |
119 | // Information captured about the inclusion graph in a translation unit. |
120 | // This includes detailed information about the direct #includes, and summary |
121 | // information about all transitive includes. |
122 | // |
123 | // It should be built incrementally with collectIncludeStructureCallback(). |
124 | // When we build the preamble, we capture and store its include structure along |
125 | // with the preamble data. When we use the preamble, we can copy its |
126 | // IncludeStructure and use another collectIncludeStructureCallback() to fill |
127 | // in any non-preamble inclusions. |
128 | class IncludeStructure { |
129 | public: |
130 | IncludeStructure() { |
131 | // Reserve HeaderID = 0 for the main file. |
132 | RealPathNames.emplace_back(); |
133 | } |
134 | |
135 | // Inserts a PPCallback and CommentHandler that visits all includes in the |
136 | // main file and populates the structure. It will also scan for IWYU pragmas |
137 | // in comments. |
138 | void collect(const CompilerInstance &CI); |
139 | |
140 | // HeaderID identifies file in the include graph. It corresponds to a |
141 | // FileEntry rather than a FileID, but stays stable across preamble & main |
142 | // file builds. |
143 | enum class : unsigned {}; |
144 | |
145 | std::optional<HeaderID> getID(const FileEntry *Entry) const; |
146 | HeaderID getOrCreateID(FileEntryRef Entry); |
147 | |
148 | StringRef (HeaderID ID) const { |
149 | assert(static_cast<unsigned>(ID) <= RealPathNames.size()); |
150 | return RealPathNames[static_cast<unsigned>(ID)]; |
151 | } |
152 | |
153 | // Return all transitively reachable files. |
154 | llvm::ArrayRef<std::string> () const { return RealPathNames; } |
155 | |
156 | // Returns includes inside the main file with the given spelling. |
157 | // Spelling should include brackets or quotes, e.g. <foo>. |
158 | llvm::SmallVector<const Inclusion *> |
159 | mainFileIncludesWithSpelling(llvm::StringRef Spelling) const; |
160 | |
161 | // Return all transitively reachable files, and their minimum include depth. |
162 | // All transitive includes (absolute paths), with their minimum include depth. |
163 | // Root --> 0, #included file --> 1, etc. |
164 | // Root is the ID of the header being visited first. |
165 | llvm::DenseMap<HeaderID, unsigned> |
166 | (HeaderID Root = MainFileID) const; |
167 | |
168 | // Maps HeaderID to the ids of the files included from it. |
169 | llvm::DenseMap<HeaderID, SmallVector<HeaderID>> IncludeChildren; |
170 | |
171 | llvm::DenseMap<tooling::stdlib::Header, llvm::SmallVector<HeaderID>> |
172 | ; |
173 | |
174 | std::vector<Inclusion> MainFileIncludes; |
175 | |
176 | // The entries of the header search path. (HeaderSearch::search_dir_range()) |
177 | // Only includes the plain-directory entries (not header maps or frameworks). |
178 | // All paths are canonical (FileManager::getCanonicalPath()). |
179 | std::vector<std::string> SearchPathsCanonical; |
180 | |
181 | // We reserve HeaderID(0) for the main file and will manually check for that |
182 | // in getID and getOrCreateID because the UniqueID is not stable when the |
183 | // content of the main file changes. |
184 | static const HeaderID MainFileID = HeaderID(0u); |
185 | |
186 | class ; |
187 | |
188 | private: |
189 | // MainFileEntry will be used to check if the queried file is the main file |
190 | // or not. |
191 | const FileEntry *MainFileEntry = nullptr; |
192 | |
193 | std::vector<std::string> RealPathNames; // In HeaderID order. |
194 | // FileEntry::UniqueID is mapped to the internal representation (HeaderID). |
195 | // Identifying files in a way that persists from preamble build to subsequent |
196 | // builds is surprisingly hard. FileID is unavailable in InclusionDirective(), |
197 | // and RealPathName and UniqueID are not preserved in |
198 | // the preamble. |
199 | llvm::DenseMap<llvm::sys::fs::UniqueID, HeaderID> UIDToIndex; |
200 | |
201 | // Maps written includes to indices in MainFileInclude for easier lookup by |
202 | // spelling. |
203 | llvm::StringMap<llvm::SmallVector<unsigned>> MainFileIncludesBySpelling; |
204 | }; |
205 | |
206 | // Calculates insertion edit for including a new header in a file. |
207 | class IncludeInserter { |
208 | public: |
209 | // If \p HeaderSearchInfo is nullptr (e.g. when compile command is |
210 | // infeasible), this will only try to insert verbatim headers, and |
211 | // include path of non-verbatim header will not be shortened. |
212 | (StringRef FileName, StringRef Code, |
213 | const format::FormatStyle &Style, StringRef BuildDir, |
214 | HeaderSearch *) |
215 | : FileName(FileName), Code(Code), BuildDir(BuildDir), |
216 | HeaderSearchInfo(HeaderSearchInfo), |
217 | Inserter(FileName, Code, Style.IncludeStyle) {} |
218 | |
219 | void addExisting(const Inclusion &Inc); |
220 | |
221 | /// Checks whether to add an #include of the header into \p File. |
222 | /// An #include will not be added if: |
223 | /// - Either \p DeclaringHeader or \p InsertedHeader is already (directly) |
224 | /// in \p Inclusions (including those included via different paths). |
225 | /// - \p DeclaringHeader or \p InsertedHeader is the same as \p File. |
226 | /// |
227 | /// \param DeclaringHeader is path of the original header corresponding to \p |
228 | /// InsertedHeader e.g. the header that declares a symbol. |
229 | /// \param InsertedHeader The preferred header to be inserted. This could be |
230 | /// the same as DeclaringHeader but must be provided. |
231 | bool (PathRef , |
232 | const HeaderFile &) const; |
233 | |
234 | /// Determines the preferred way to #include a file, taking into account the |
235 | /// search path. Usually this will prefer a shorter representation like |
236 | /// 'Foo/Bar.h' over a longer one like 'Baz/include/Foo/Bar.h'. |
237 | /// |
238 | /// \param InsertedHeader The preferred header to be inserted. |
239 | /// |
240 | /// \param IncludingFile is the absolute path of the file that InsertedHeader |
241 | /// will be inserted. |
242 | /// |
243 | /// \return A quoted "path" or <path> to be included, or std::nullopt if it |
244 | /// couldn't be shortened. |
245 | std::optional<std::string> |
246 | (const HeaderFile &, |
247 | llvm::StringRef IncludingFile) const; |
248 | |
249 | /// Calculates an edit that inserts \p VerbatimHeader into code. If the header |
250 | /// is already included, this returns std::nullopt. |
251 | std::optional<TextEdit> insert(llvm::StringRef , |
252 | tooling::IncludeDirective Directive) const; |
253 | |
254 | private: |
255 | StringRef FileName; |
256 | StringRef Code; |
257 | StringRef BuildDir; |
258 | HeaderSearch * = nullptr; |
259 | llvm::StringSet<> ; // Both written and resolved. |
260 | tooling::HeaderIncludes Inserter; // Computers insertion replacement. |
261 | }; |
262 | |
263 | } // namespace clangd |
264 | } // namespace clang |
265 | |
266 | namespace llvm { |
267 | |
268 | // Support HeaderIDs as DenseMap keys. |
269 | template <> struct DenseMapInfo<clang::clangd::IncludeStructure::HeaderID> { |
270 | static inline clang::clangd::IncludeStructure::HeaderID () { |
271 | return static_cast<clang::clangd::IncludeStructure::HeaderID>(-1); |
272 | } |
273 | |
274 | static inline clang::clangd::IncludeStructure::HeaderID () { |
275 | return static_cast<clang::clangd::IncludeStructure::HeaderID>(-2); |
276 | } |
277 | |
278 | static unsigned |
279 | (const clang::clangd::IncludeStructure::HeaderID &Tag) { |
280 | return hash_value(value: static_cast<unsigned>(Tag)); |
281 | } |
282 | |
283 | static bool (const clang::clangd::IncludeStructure::HeaderID &LHS, |
284 | const clang::clangd::IncludeStructure::HeaderID &RHS) { |
285 | return LHS == RHS; |
286 | } |
287 | }; |
288 | |
289 | } // namespace llvm |
290 | |
291 | #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEADERS_H |
292 | |