1 | //===--- SymbolCollector.cpp -------------------------------------*- 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 | #include "SymbolCollector.h" |
10 | #include "AST.h" |
11 | #include "CodeComplete.h" |
12 | #include "CodeCompletionStrings.h" |
13 | #include "ExpectedTypes.h" |
14 | #include "SourceCode.h" |
15 | #include "URI.h" |
16 | #include "clang-include-cleaner/Analysis.h" |
17 | #include "clang-include-cleaner/IncludeSpeller.h" |
18 | #include "clang-include-cleaner/Record.h" |
19 | #include "clang-include-cleaner/Types.h" |
20 | #include "index/CanonicalIncludes.h" |
21 | #include "index/Relation.h" |
22 | #include "index/Symbol.h" |
23 | #include "index/SymbolID.h" |
24 | #include "index/SymbolLocation.h" |
25 | #include "clang/AST/Decl.h" |
26 | #include "clang/AST/DeclBase.h" |
27 | #include "clang/AST/DeclObjC.h" |
28 | #include "clang/AST/DeclTemplate.h" |
29 | #include "clang/AST/DeclarationName.h" |
30 | #include "clang/AST/Expr.h" |
31 | #include "clang/Basic/FileEntry.h" |
32 | #include "clang/Basic/LangOptions.h" |
33 | #include "clang/Basic/SourceLocation.h" |
34 | #include "clang/Basic/SourceManager.h" |
35 | #include "clang/Index/IndexSymbol.h" |
36 | #include "clang/Lex/Preprocessor.h" |
37 | #include "clang/Lex/Token.h" |
38 | #include "clang/Tooling/Inclusions/HeaderAnalysis.h" |
39 | #include "clang/Tooling/Inclusions/StandardLibrary.h" |
40 | #include "llvm/ADT/ArrayRef.h" |
41 | #include "llvm/ADT/DenseMap.h" |
42 | #include "llvm/ADT/SmallVector.h" |
43 | #include "llvm/ADT/StringRef.h" |
44 | #include "llvm/Support/ErrorHandling.h" |
45 | #include "llvm/Support/FileSystem.h" |
46 | #include "llvm/Support/Path.h" |
47 | #include <cassert> |
48 | #include <memory> |
49 | #include <optional> |
50 | #include <string> |
51 | #include <utility> |
52 | |
53 | namespace clang { |
54 | namespace clangd { |
55 | namespace { |
56 | |
57 | /// If \p ND is a template specialization, returns the described template. |
58 | /// Otherwise, returns \p ND. |
59 | const NamedDecl &getTemplateOrThis(const NamedDecl &ND) { |
60 | if (auto *T = ND.getDescribedTemplate()) |
61 | return *T; |
62 | return ND; |
63 | } |
64 | |
65 | // Checks whether the decl is a private symbol in a header generated by |
66 | // protobuf compiler. |
67 | // FIXME: make filtering extensible when there are more use cases for symbol |
68 | // filters. |
69 | bool isPrivateProtoDecl(const NamedDecl &ND) { |
70 | const auto &SM = ND.getASTContext().getSourceManager(); |
71 | if (!isProtoFile(nameLocation(ND, SM), SM)) |
72 | return false; |
73 | |
74 | // ND without identifier can be operators. |
75 | if (ND.getIdentifier() == nullptr) |
76 | return false; |
77 | auto Name = ND.getIdentifier()->getName(); |
78 | if (!Name.contains(C: '_')) |
79 | return false; |
80 | // Nested proto entities (e.g. Message::Nested) have top-level decls |
81 | // that shouldn't be used (Message_Nested). Ignore them completely. |
82 | // The nested entities are dangling type aliases, we may want to reconsider |
83 | // including them in the future. |
84 | // For enum constants, SOME_ENUM_CONSTANT is not private and should be |
85 | // indexed. Outer_INNER is private. This heuristic relies on naming style, it |
86 | // will include OUTER_INNER and exclude some_enum_constant. |
87 | // FIXME: the heuristic relies on naming style (i.e. no underscore in |
88 | // user-defined names) and can be improved. |
89 | return (ND.getKind() != Decl::EnumConstant) || llvm::any_of(Range&: Name, P: islower); |
90 | } |
91 | |
92 | // We only collect #include paths for symbols that are suitable for global code |
93 | // completion, except for namespaces since #include path for a namespace is hard |
94 | // to define. |
95 | Symbol::IncludeDirective shouldCollectIncludePath(index::SymbolKind Kind) { |
96 | using SK = index::SymbolKind; |
97 | switch (Kind) { |
98 | case SK::Macro: |
99 | case SK::Enum: |
100 | case SK::Struct: |
101 | case SK::Class: |
102 | case SK::Union: |
103 | case SK::TypeAlias: |
104 | case SK::Using: |
105 | case SK::Function: |
106 | case SK::Variable: |
107 | case SK::EnumConstant: |
108 | case SK::Concept: |
109 | return Symbol::Include | Symbol::Import; |
110 | case SK::Protocol: |
111 | return Symbol::Import; |
112 | default: |
113 | return Symbol::Invalid; |
114 | } |
115 | } |
116 | |
117 | // Return the symbol range of the token at \p TokLoc. |
118 | std::pair<SymbolLocation::Position, SymbolLocation::Position> |
119 | getTokenRange(SourceLocation TokLoc, const SourceManager &SM, |
120 | const LangOptions &LangOpts) { |
121 | auto CreatePosition = [&SM](SourceLocation Loc) { |
122 | auto LSPLoc = sourceLocToPosition(SM, Loc); |
123 | SymbolLocation::Position Pos; |
124 | Pos.setLine(LSPLoc.line); |
125 | Pos.setColumn(LSPLoc.character); |
126 | return Pos; |
127 | }; |
128 | |
129 | auto TokenLength = clang::Lexer::MeasureTokenLength(Loc: TokLoc, SM, LangOpts); |
130 | return {CreatePosition(TokLoc), |
131 | CreatePosition(TokLoc.getLocWithOffset(Offset: TokenLength))}; |
132 | } |
133 | |
134 | // Checks whether \p ND is a good candidate to be the *canonical* declaration of |
135 | // its symbol (e.g. a go-to-declaration target). This overrides the default of |
136 | // using Clang's canonical declaration, which is the first in the TU. |
137 | // |
138 | // Example: preferring a class declaration over its forward declaration. |
139 | bool isPreferredDeclaration(const NamedDecl &ND, index::SymbolRoleSet Roles) { |
140 | const auto &SM = ND.getASTContext().getSourceManager(); |
141 | if (isa<TagDecl>(Val: ND)) |
142 | return (Roles & static_cast<unsigned>(index::SymbolRole::Definition)) && |
143 | !isInsideMainFile(ND.getLocation(), SM); |
144 | if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(Val: &ND)) |
145 | return ID->isThisDeclarationADefinition(); |
146 | if (const auto *PD = dyn_cast<ObjCProtocolDecl>(Val: &ND)) |
147 | return PD->isThisDeclarationADefinition(); |
148 | return false; |
149 | } |
150 | |
151 | RefKind toRefKind(index::SymbolRoleSet Roles, bool Spelled = false) { |
152 | RefKind Result = RefKind::Unknown; |
153 | if (Roles & static_cast<unsigned>(index::SymbolRole::Declaration)) |
154 | Result |= RefKind::Declaration; |
155 | if (Roles & static_cast<unsigned>(index::SymbolRole::Definition)) |
156 | Result |= RefKind::Definition; |
157 | if (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) |
158 | Result |= RefKind::Reference; |
159 | if (Spelled) |
160 | Result |= RefKind::Spelled; |
161 | return Result; |
162 | } |
163 | |
164 | std::optional<RelationKind> indexableRelation(const index::SymbolRelation &R) { |
165 | if (R.Roles & static_cast<unsigned>(index::SymbolRole::RelationBaseOf)) |
166 | return RelationKind::BaseOf; |
167 | if (R.Roles & static_cast<unsigned>(index::SymbolRole::RelationOverrideOf)) |
168 | return RelationKind::OverriddenBy; |
169 | return std::nullopt; |
170 | } |
171 | |
172 | // Check if there is an exact spelling of \p ND at \p Loc. |
173 | bool isSpelled(SourceLocation Loc, const NamedDecl &ND) { |
174 | auto Name = ND.getDeclName(); |
175 | const auto NameKind = Name.getNameKind(); |
176 | if (NameKind != DeclarationName::Identifier && |
177 | NameKind != DeclarationName::CXXConstructorName && |
178 | NameKind != DeclarationName::ObjCZeroArgSelector && |
179 | NameKind != DeclarationName::ObjCOneArgSelector && |
180 | NameKind != DeclarationName::ObjCMultiArgSelector) |
181 | return false; |
182 | const auto &AST = ND.getASTContext(); |
183 | const auto &SM = AST.getSourceManager(); |
184 | const auto &LO = AST.getLangOpts(); |
185 | clang::Token Tok; |
186 | if (clang::Lexer::getRawToken(Loc, Result&: Tok, SM: SM, LangOpts: LO)) |
187 | return false; |
188 | auto TokSpelling = clang::Lexer::getSpelling(Tok, SM, LO); |
189 | if (const auto *MD = dyn_cast<ObjCMethodDecl>(Val: &ND)) |
190 | return TokSpelling == MD->getSelector().getNameForSlot(argIndex: 0); |
191 | return TokSpelling == Name.getAsString(); |
192 | } |
193 | } // namespace |
194 | |
195 | // Encapsulates decisions about how to record header paths in the index, |
196 | // including filename normalization, URI conversion etc. |
197 | // Expensive checks are cached internally. |
198 | class SymbolCollector:: { |
199 | struct { |
200 | // Spelling for the public umbrella header, e.g. <Foundation/Foundation.h> |
201 | std::optional<std::string> ; |
202 | // Spelling for the private umbrella header, e.g. |
203 | // <Foundation/Foundation_Private.h> |
204 | std::optional<std::string> ; |
205 | }; |
206 | // Weird double-indirect access to PP, which might not be ready yet when |
207 | // HeaderFiles is created but will be by the time it's used. |
208 | // (IndexDataConsumer::setPreprocessor can happen before or after initialize) |
209 | Preprocessor *&; |
210 | const SourceManager &; |
211 | const include_cleaner::PragmaIncludes *; |
212 | llvm::StringRef ; |
213 | llvm::DenseMap<const FileEntry *, const std::string *> ; |
214 | llvm::StringMap<std::string> ; |
215 | llvm::DenseMap<FileID, llvm::StringRef> ; |
216 | llvm::StringMap<std::string> ; |
217 | llvm::StringMap<FrameworkUmbrellaSpelling> |
218 | ; |
219 | |
220 | public: |
221 | (Preprocessor *&PP, const SourceManager &SM, |
222 | const SymbolCollector::Options &Opts) |
223 | : PP(PP), SM(SM), PI(Opts.PragmaIncludes), FallbackDir(Opts.FallbackDir) { |
224 | } |
225 | |
226 | // Returns a canonical URI for the file \p FE. |
227 | // We attempt to make the path absolute first. |
228 | const std::string &(const FileEntryRef FE) { |
229 | auto R = CacheFEToURI.try_emplace(Key: FE); |
230 | if (R.second) { |
231 | auto CanonPath = getCanonicalPath(F: FE, FileMgr&: SM.getFileManager()); |
232 | R.first->second = &toURIInternal(Path: CanonPath ? *CanonPath : FE.getName()); |
233 | } |
234 | return *R.first->second; |
235 | } |
236 | |
237 | // Returns a canonical URI for \p Path. |
238 | // If the file is in the FileManager, use that to canonicalize the path. |
239 | // We attempt to make the path absolute in any case. |
240 | const std::string &(llvm::StringRef Path) { |
241 | if (auto File = SM.getFileManager().getFileRef(Filename: Path)) |
242 | return toURI(FE: *File); |
243 | return toURIInternal(Path); |
244 | } |
245 | |
246 | // Gets a canonical include (URI of the header or <header> or "header") for |
247 | // header of \p FID (which should usually be the *expansion* file). |
248 | // This does not account for any per-symbol overrides! |
249 | // Returns "" if includes should not be inserted for this file. |
250 | llvm::StringRef (FileID FID) { |
251 | auto R = CacheFIDToInclude.try_emplace(Key: FID); |
252 | if (R.second) |
253 | R.first->second = getIncludeHeaderUncached(FID); |
254 | return R.first->second; |
255 | } |
256 | |
257 | // If a file is mapped by canonical headers, use that mapping, regardless |
258 | // of whether it's an otherwise-good header (header guards etc). |
259 | llvm::StringRef (llvm::StringRef ) { |
260 | if (!PP) |
261 | return "" ; |
262 | // Populate the system header mapping as late as possible to |
263 | // ensure the preprocessor has been set already. |
264 | CanonicalIncludes ; |
265 | SysHeaderMapping.addSystemHeadersMapping(Language: PP->getLangOpts()); |
266 | auto Canonical = SysHeaderMapping.mapHeader(HeaderPath); |
267 | if (Canonical.empty()) |
268 | return "" ; |
269 | // If we had a mapping, always use it. |
270 | assert(Canonical.starts_with("<" ) || Canonical.starts_with("\"" )); |
271 | return Canonical; |
272 | } |
273 | |
274 | private: |
275 | // This takes care of making paths absolute and path->URI caching, but no |
276 | // FileManager-based canonicalization. |
277 | const std::string &(llvm::StringRef Path) { |
278 | auto R = CachePathToURI.try_emplace(Key: Path); |
279 | if (R.second) { |
280 | llvm::SmallString<256> AbsPath = Path; |
281 | if (!llvm::sys::path::is_absolute(path: AbsPath) && !FallbackDir.empty()) |
282 | llvm::sys::fs::make_absolute(current_directory: FallbackDir, path&: AbsPath); |
283 | assert(llvm::sys::path::is_absolute(AbsPath) && |
284 | "If the VFS can't make paths absolute, a FallbackDir must be " |
285 | "provided" ); |
286 | llvm::sys::path::remove_dots(path&: AbsPath, /*remove_dot_dot=*/true); |
287 | R.first->second = URI::create(AbsolutePath: AbsPath).toString(); |
288 | } |
289 | return R.first->second; |
290 | } |
291 | |
292 | struct { |
293 | // Path to the framework directory containing the Headers/PrivateHeaders |
294 | // directories e.g. /Frameworks/Foundation.framework/ |
295 | llvm::StringRef ; |
296 | // Subpath relative to the Headers or PrivateHeaders dir, e.g. NSObject.h |
297 | // Note: This is NOT relative to the `HeadersParentDir`. |
298 | llvm::StringRef ; |
299 | // Whether this header is under the PrivateHeaders dir |
300 | bool ; |
301 | }; |
302 | |
303 | std::optional<FrameworkHeaderPath> |
304 | (llvm::StringRef Path) { |
305 | using namespace llvm::sys; |
306 | path::reverse_iterator I = path::rbegin(path: Path); |
307 | path::reverse_iterator Prev = I; |
308 | path::reverse_iterator E = path::rend(path: Path); |
309 | while (I != E) { |
310 | if (*I == "Headers" ) { |
311 | FrameworkHeaderPath ; |
312 | HeaderPath.HeadersParentDir = Path.substr(Start: 0, N: I - E); |
313 | HeaderPath.HeaderSubpath = Path.substr(Start: Prev - E); |
314 | HeaderPath.IsPrivateHeader = false; |
315 | return HeaderPath; |
316 | } |
317 | if (*I == "PrivateHeaders" ) { |
318 | FrameworkHeaderPath ; |
319 | HeaderPath.HeadersParentDir = Path.substr(Start: 0, N: I - E); |
320 | HeaderPath.HeaderSubpath = Path.substr(Start: Prev - E); |
321 | HeaderPath.IsPrivateHeader = true; |
322 | return HeaderPath; |
323 | } |
324 | Prev = I; |
325 | ++I; |
326 | } |
327 | // Unexpected, must not be a framework header. |
328 | return std::nullopt; |
329 | } |
330 | |
331 | // Frameworks typically have an umbrella header of the same name, e.g. |
332 | // <Foundation/Foundation.h> instead of <Foundation/NSObject.h> or |
333 | // <Foundation/Foundation_Private.h> instead of |
334 | // <Foundation/NSObject_Private.h> which should be used instead of directly |
335 | // importing the header. |
336 | std::optional<std::string> |
337 | (llvm::StringRef Framework, |
338 | const HeaderSearch &HS, |
339 | FrameworkHeaderPath &) { |
340 | auto Res = CacheFrameworkToUmbrellaHeaderSpelling.try_emplace(Key: Framework); |
341 | auto *CachedSpelling = &Res.first->second; |
342 | if (!Res.second) { |
343 | return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader |
344 | : CachedSpelling->PublicHeader; |
345 | } |
346 | SmallString<256> UmbrellaPath(HeaderPath.HeadersParentDir); |
347 | llvm::sys::path::append(path&: UmbrellaPath, a: "Headers" , b: Framework + ".h" ); |
348 | |
349 | llvm::vfs::Status Status; |
350 | auto StatErr = HS.getFileMgr().getNoncachedStatValue(Path: UmbrellaPath, Result&: Status); |
351 | if (!StatErr) |
352 | CachedSpelling->PublicHeader = llvm::formatv(Fmt: "<{0}/{0}.h>" , Vals&: Framework); |
353 | |
354 | UmbrellaPath = HeaderPath.HeadersParentDir; |
355 | llvm::sys::path::append(path&: UmbrellaPath, a: "PrivateHeaders" , |
356 | b: Framework + "_Private.h" ); |
357 | |
358 | StatErr = HS.getFileMgr().getNoncachedStatValue(Path: UmbrellaPath, Result&: Status); |
359 | if (!StatErr) |
360 | CachedSpelling->PrivateHeader = |
361 | llvm::formatv(Fmt: "<{0}/{0}_Private.h>" , Vals&: Framework); |
362 | |
363 | return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader |
364 | : CachedSpelling->PublicHeader; |
365 | } |
366 | |
367 | // Compute the framework include spelling for `FE` which is in a framework |
368 | // named `Framework`, e.g. `NSObject.h` in framework `Foundation` would |
369 | // give <Foundation/Foundation.h> if the umbrella header exists, otherwise |
370 | // <Foundation/NSObject.h>. |
371 | std::optional<llvm::StringRef> |
372 | (FileEntryRef FE, llvm::StringRef Framework, |
373 | HeaderSearch &HS) { |
374 | auto Res = CachePathToFrameworkSpelling.try_emplace(Key: FE.getName()); |
375 | auto * = &Res.first->second; |
376 | if (!Res.second) |
377 | return llvm::StringRef(*CachedHeaderSpelling); |
378 | |
379 | auto = splitFrameworkHeaderPath(Path: FE.getName()); |
380 | if (!HeaderPath) { |
381 | // Unexpected: must not be a proper framework header, don't cache the |
382 | // failure. |
383 | CachePathToFrameworkSpelling.erase(I: Res.first); |
384 | return std::nullopt; |
385 | } |
386 | if (auto UmbrellaSpelling = |
387 | getFrameworkUmbrellaSpelling(Framework, HS, HeaderPath&: *HeaderPath)) { |
388 | *CachedHeaderSpelling = *UmbrellaSpelling; |
389 | return llvm::StringRef(*CachedHeaderSpelling); |
390 | } |
391 | |
392 | *CachedHeaderSpelling = |
393 | llvm::formatv(Fmt: "<{0}/{1}>" , Vals&: Framework, Vals&: HeaderPath->HeaderSubpath).str(); |
394 | return llvm::StringRef(*CachedHeaderSpelling); |
395 | } |
396 | |
397 | llvm::StringRef (FileID FID) { |
398 | const auto FE = SM.getFileEntryRefForID(FID); |
399 | if (!FE || FE->getName().empty()) |
400 | return "" ; |
401 | |
402 | if (auto Verbatim = PI->getPublic(File: *FE); !Verbatim.empty()) |
403 | return Verbatim; |
404 | |
405 | llvm::StringRef Filename = FE->getName(); |
406 | if (auto Canonical = mapCanonical(HeaderPath: Filename); !Canonical.empty()) |
407 | return Canonical; |
408 | |
409 | // Framework headers are spelled as <FrameworkName/Foo.h>, not |
410 | // "path/FrameworkName.framework/Headers/Foo.h". |
411 | auto &HS = PP->getHeaderSearchInfo(); |
412 | if (const auto *HFI = HS.getExistingFileInfo(FE: *FE)) |
413 | if (!HFI->Framework.empty()) |
414 | if (auto Spelling = |
415 | getFrameworkHeaderIncludeSpelling(FE: *FE, Framework: HFI->Framework, HS)) |
416 | return *Spelling; |
417 | |
418 | if (!tooling::isSelfContainedHeader(FE: *FE, SM: PP->getSourceManager(), |
419 | HeaderInfo: PP->getHeaderSearchInfo())) { |
420 | // A .inc or .def file is often included into a real header to define |
421 | // symbols (e.g. LLVM tablegen files). |
422 | if (Filename.ends_with(Suffix: ".inc" ) || Filename.ends_with(Suffix: ".def" )) |
423 | // Don't use cache reentrantly due to iterator invalidation. |
424 | return getIncludeHeaderUncached(FID: SM.getFileID(SpellingLoc: SM.getIncludeLoc(FID))); |
425 | // Conservatively refuse to insert #includes to files without guards. |
426 | return "" ; |
427 | } |
428 | // Standard case: just insert the file itself. |
429 | return toURI(FE: *FE); |
430 | } |
431 | }; |
432 | |
433 | // Return the symbol location of the token at \p TokLoc. |
434 | std::optional<SymbolLocation> |
435 | SymbolCollector::getTokenLocation(SourceLocation TokLoc) { |
436 | const auto &SM = ASTCtx->getSourceManager(); |
437 | const auto FE = SM.getFileEntryRefForID(FID: SM.getFileID(SpellingLoc: TokLoc)); |
438 | if (!FE) |
439 | return std::nullopt; |
440 | |
441 | SymbolLocation Result; |
442 | Result.FileURI = HeaderFileURIs->toURI(FE: *FE).c_str(); |
443 | auto Range = getTokenRange(TokLoc, SM, LangOpts: ASTCtx->getLangOpts()); |
444 | Result.Start = Range.first; |
445 | Result.End = Range.second; |
446 | |
447 | return Result; |
448 | } |
449 | |
450 | SymbolCollector::SymbolCollector(Options Opts) : Opts(std::move(Opts)) {} |
451 | SymbolCollector::~SymbolCollector() = default; |
452 | |
453 | void SymbolCollector::initialize(ASTContext &Ctx) { |
454 | ASTCtx = &Ctx; |
455 | HeaderFileURIs = std::make_unique<HeaderFileURICache>( |
456 | args&: this->PP, args&: ASTCtx->getSourceManager(), args&: Opts); |
457 | CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>(); |
458 | CompletionTUInfo = |
459 | std::make_unique<CodeCompletionTUInfo>(args&: CompletionAllocator); |
460 | } |
461 | |
462 | bool SymbolCollector::shouldCollectSymbol(const NamedDecl &ND, |
463 | const ASTContext &ASTCtx, |
464 | const Options &Opts, |
465 | bool IsMainFileOnly) { |
466 | // Skip anonymous declarations, e.g (anonymous enum/class/struct). |
467 | if (ND.getDeclName().isEmpty()) |
468 | return false; |
469 | |
470 | // Skip main-file symbols if we are not collecting them. |
471 | if (IsMainFileOnly && !Opts.CollectMainFileSymbols) |
472 | return false; |
473 | |
474 | // Skip symbols in anonymous namespaces in header files. |
475 | if (!IsMainFileOnly && ND.isInAnonymousNamespace()) |
476 | return false; |
477 | |
478 | // For function local symbols, index only classes and its member functions. |
479 | if (index::isFunctionLocalSymbol(&ND)) |
480 | return isa<RecordDecl>(Val: ND) || |
481 | (ND.isCXXInstanceMember() && ND.isFunctionOrFunctionTemplate()); |
482 | |
483 | // We want most things but not "local" symbols such as symbols inside |
484 | // FunctionDecl, BlockDecl, ObjCMethodDecl and OMPDeclareReductionDecl. |
485 | // FIXME: Need a matcher for ExportDecl in order to include symbols declared |
486 | // within an export. |
487 | const auto *DeclCtx = ND.getDeclContext(); |
488 | switch (DeclCtx->getDeclKind()) { |
489 | case Decl::TranslationUnit: |
490 | case Decl::Namespace: |
491 | case Decl::LinkageSpec: |
492 | case Decl::Enum: |
493 | case Decl::ObjCProtocol: |
494 | case Decl::ObjCInterface: |
495 | case Decl::ObjCCategory: |
496 | case Decl::ObjCCategoryImpl: |
497 | case Decl::ObjCImplementation: |
498 | break; |
499 | default: |
500 | // Record has a few derivations (e.g. CXXRecord, Class specialization), it's |
501 | // easier to cast. |
502 | if (!isa<RecordDecl>(DeclCtx)) |
503 | return false; |
504 | } |
505 | |
506 | // Avoid indexing internal symbols in protobuf generated headers. |
507 | if (isPrivateProtoDecl(ND)) |
508 | return false; |
509 | if (!Opts.CollectReserved && |
510 | (hasReservedName(ND) || hasReservedScope(*ND.getDeclContext())) && |
511 | ASTCtx.getSourceManager().isInSystemHeader(Loc: ND.getLocation())) |
512 | return false; |
513 | |
514 | return true; |
515 | } |
516 | |
517 | const Decl * |
518 | SymbolCollector::getRefContainer(const Decl *Enclosing, |
519 | const SymbolCollector::Options &Opts) { |
520 | while (Enclosing) { |
521 | const auto *ND = dyn_cast<NamedDecl>(Val: Enclosing); |
522 | if (ND && shouldCollectSymbol(ND: *ND, ASTCtx: ND->getASTContext(), Opts, IsMainFileOnly: true)) { |
523 | break; |
524 | } |
525 | Enclosing = dyn_cast_or_null<Decl>(Val: Enclosing->getDeclContext()); |
526 | } |
527 | return Enclosing; |
528 | } |
529 | |
530 | // Always return true to continue indexing. |
531 | bool SymbolCollector::handleDeclOccurrence( |
532 | const Decl *D, index::SymbolRoleSet Roles, |
533 | llvm::ArrayRef<index::SymbolRelation> Relations, SourceLocation Loc, |
534 | index::IndexDataConsumer::ASTNodeInfo ASTNode) { |
535 | assert(ASTCtx && PP && HeaderFileURIs); |
536 | assert(CompletionAllocator && CompletionTUInfo); |
537 | assert(ASTNode.OrigD); |
538 | // Indexing API puts canonical decl into D, which might not have a valid |
539 | // source location for implicit/built-in decls. Fallback to original decl in |
540 | // such cases. |
541 | if (D->getLocation().isInvalid()) |
542 | D = ASTNode.OrigD; |
543 | // If OrigD is an declaration associated with a friend declaration and it's |
544 | // not a definition, skip it. Note that OrigD is the occurrence that the |
545 | // collector is currently visiting. |
546 | if ((ASTNode.OrigD->getFriendObjectKind() != |
547 | Decl::FriendObjectKind::FOK_None) && |
548 | !(Roles & static_cast<unsigned>(index::SymbolRole::Definition))) |
549 | return true; |
550 | // A declaration created for a friend declaration should not be used as the |
551 | // canonical declaration in the index. Use OrigD instead, unless we've already |
552 | // picked a replacement for D |
553 | if (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) |
554 | D = CanonicalDecls.try_emplace(Key: D, Args&: ASTNode.OrigD).first->second; |
555 | // Flag to mark that D should be considered canonical meaning its declaration |
556 | // will override any previous declaration for the Symbol. |
557 | bool DeclIsCanonical = false; |
558 | // Avoid treating ObjCImplementationDecl as a canonical declaration if it has |
559 | // a corresponding non-implicit and non-forward declared ObjcInterfaceDecl. |
560 | if (const auto *IID = dyn_cast<ObjCImplementationDecl>(Val: D)) { |
561 | DeclIsCanonical = true; |
562 | if (const auto *CID = IID->getClassInterface()) |
563 | if (const auto *DD = CID->getDefinition()) |
564 | if (!DD->isImplicitInterfaceDecl()) |
565 | D = DD; |
566 | } |
567 | // Avoid treating ObjCCategoryImplDecl as a canonical declaration in favor of |
568 | // its ObjCCategoryDecl if it has one. |
569 | if (const auto *CID = dyn_cast<ObjCCategoryImplDecl>(Val: D)) { |
570 | DeclIsCanonical = true; |
571 | if (const auto *CD = CID->getCategoryDecl()) |
572 | D = CD; |
573 | } |
574 | const NamedDecl *ND = dyn_cast<NamedDecl>(Val: D); |
575 | if (!ND) |
576 | return true; |
577 | |
578 | auto ID = getSymbolIDCached(ND); |
579 | if (!ID) |
580 | return true; |
581 | |
582 | // Mark D as referenced if this is a reference coming from the main file. |
583 | // D may not be an interesting symbol, but it's cheaper to check at the end. |
584 | auto &SM = ASTCtx->getSourceManager(); |
585 | if (Opts.CountReferences && |
586 | (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) && |
587 | SM.getFileID(SpellingLoc: SM.getSpellingLoc(Loc)) == SM.getMainFileID()) |
588 | ReferencedSymbols.insert(ID); |
589 | |
590 | // ND is the canonical (i.e. first) declaration. If it's in the main file |
591 | // (which is not a header), then no public declaration was visible, so assume |
592 | // it's main-file only. |
593 | bool IsMainFileOnly = |
594 | SM.isWrittenInMainFile(Loc: SM.getExpansionLoc(Loc: ND->getBeginLoc())) && |
595 | !isHeaderFile(FileName: SM.getFileEntryRefForID(FID: SM.getMainFileID())->getName(), |
596 | LangOpts: ASTCtx->getLangOpts()); |
597 | // In C, printf is a redecl of an implicit builtin! So check OrigD instead. |
598 | if (ASTNode.OrigD->isImplicit() || |
599 | !shouldCollectSymbol(ND: *ND, ASTCtx: *ASTCtx, Opts, IsMainFileOnly)) |
600 | return true; |
601 | |
602 | // Note: we need to process relations for all decl occurrences, including |
603 | // refs, because the indexing code only populates relations for specific |
604 | // occurrences. For example, RelationBaseOf is only populated for the |
605 | // occurrence inside the base-specifier. |
606 | processRelations(ND: *ND, ID: ID, Relations); |
607 | |
608 | bool CollectRef = static_cast<bool>(Opts.RefFilter & toRefKind(Roles)); |
609 | // Unlike other fields, e.g. Symbols (which use spelling locations), we use |
610 | // file locations for references (as it aligns the behavior of clangd's |
611 | // AST-based xref). |
612 | // FIXME: we should try to use the file locations for other fields. |
613 | if (CollectRef && |
614 | (!IsMainFileOnly || Opts.CollectMainFileRefs || |
615 | ND->isExternallyVisible()) && |
616 | !isa<NamespaceDecl>(Val: ND)) { |
617 | auto FileLoc = SM.getFileLoc(Loc); |
618 | auto FID = SM.getFileID(SpellingLoc: FileLoc); |
619 | if (Opts.RefsInHeaders || FID == SM.getMainFileID()) { |
620 | addRef(ID: ID, SR: SymbolRef{.Loc: FileLoc, .FID: FID, .Roles: Roles, |
621 | .Container: getRefContainer(Enclosing: ASTNode.Parent, Opts), |
622 | .Spelled: isSpelled(Loc: FileLoc, ND: *ND)}); |
623 | } |
624 | } |
625 | // Don't continue indexing if this is a mere reference. |
626 | if (!(Roles & (static_cast<unsigned>(index::SymbolRole::Declaration) | |
627 | static_cast<unsigned>(index::SymbolRole::Definition)))) |
628 | return true; |
629 | |
630 | // FIXME: ObjCPropertyDecl are not properly indexed here: |
631 | // - ObjCPropertyDecl may have an OrigD of ObjCPropertyImplDecl, which is |
632 | // not a NamedDecl. |
633 | auto *OriginalDecl = dyn_cast<NamedDecl>(Val: ASTNode.OrigD); |
634 | if (!OriginalDecl) |
635 | return true; |
636 | |
637 | const Symbol *BasicSymbol = Symbols.find(ID: ID); |
638 | if (isPreferredDeclaration(ND: *OriginalDecl, Roles)) |
639 | // If OriginalDecl is preferred, replace/create the existing canonical |
640 | // declaration (e.g. a class forward declaration). There should be at most |
641 | // one duplicate as we expect to see only one preferred declaration per |
642 | // TU, because in practice they are definitions. |
643 | BasicSymbol = addDeclaration(*OriginalDecl, std::move(ID), IsMainFileSymbol: IsMainFileOnly); |
644 | else if (!BasicSymbol || DeclIsCanonical) |
645 | BasicSymbol = addDeclaration(*ND, std::move(ID), IsMainFileSymbol: IsMainFileOnly); |
646 | |
647 | if (Roles & static_cast<unsigned>(index::SymbolRole::Definition)) |
648 | addDefinition(*OriginalDecl, DeclSymbol: *BasicSymbol); |
649 | |
650 | return true; |
651 | } |
652 | |
653 | void SymbolCollector::handleMacros(const MainFileMacros &MacroRefsToIndex) { |
654 | assert(HeaderFileURIs && PP); |
655 | const auto &SM = PP->getSourceManager(); |
656 | const auto MainFileEntryRef = SM.getFileEntryRefForID(FID: SM.getMainFileID()); |
657 | assert(MainFileEntryRef); |
658 | |
659 | const std::string &MainFileURI = HeaderFileURIs->toURI(FE: *MainFileEntryRef); |
660 | // Add macro references. |
661 | for (const auto &IDToRefs : MacroRefsToIndex.MacroRefs) { |
662 | for (const auto &MacroRef : IDToRefs.second) { |
663 | const auto &Range = MacroRef.toRange(SM); |
664 | bool IsDefinition = MacroRef.IsDefinition; |
665 | Ref R; |
666 | R.Location.Start.setLine(Range.start.line); |
667 | R.Location.Start.setColumn(Range.start.character); |
668 | R.Location.End.setLine(Range.end.line); |
669 | R.Location.End.setColumn(Range.end.character); |
670 | R.Location.FileURI = MainFileURI.c_str(); |
671 | R.Kind = IsDefinition ? RefKind::Definition : RefKind::Reference; |
672 | Refs.insert(ID: IDToRefs.first, S: R); |
673 | if (IsDefinition) { |
674 | Symbol S; |
675 | S.ID = IDToRefs.first; |
676 | auto StartLoc = cantFail(ValOrErr: sourceLocationInMainFile(SM, P: Range.start)); |
677 | auto EndLoc = cantFail(ValOrErr: sourceLocationInMainFile(SM, P: Range.end)); |
678 | S.Name = toSourceCode(SM, R: SourceRange(StartLoc, EndLoc)); |
679 | S.SymInfo.Kind = index::SymbolKind::Macro; |
680 | S.SymInfo.SubKind = index::SymbolSubKind::None; |
681 | S.SymInfo.Properties = index::SymbolPropertySet(); |
682 | S.SymInfo.Lang = index::SymbolLanguage::C; |
683 | S.Origin = Opts.Origin; |
684 | S.CanonicalDeclaration = R.Location; |
685 | // Make the macro visible for code completion if main file is an |
686 | // include-able header. |
687 | if (!HeaderFileURIs->getIncludeHeader(FID: SM.getMainFileID()).empty()) { |
688 | S.Flags |= Symbol::IndexedForCodeCompletion; |
689 | S.Flags |= Symbol::VisibleOutsideFile; |
690 | } |
691 | Symbols.insert(S); |
692 | } |
693 | } |
694 | } |
695 | } |
696 | |
697 | bool SymbolCollector::handleMacroOccurrence(const IdentifierInfo *Name, |
698 | const MacroInfo *MI, |
699 | index::SymbolRoleSet Roles, |
700 | SourceLocation Loc) { |
701 | assert(PP); |
702 | // Builtin macros don't have useful locations and aren't needed in completion. |
703 | if (MI->isBuiltinMacro()) |
704 | return true; |
705 | |
706 | const auto &SM = PP->getSourceManager(); |
707 | auto DefLoc = MI->getDefinitionLoc(); |
708 | // Also avoid storing macros that aren't defined in any file, i.e. predefined |
709 | // macros like __DBL_MIN__ and those defined on the command line. |
710 | if (SM.isWrittenInBuiltinFile(Loc: DefLoc) || |
711 | SM.isWrittenInCommandLineFile(Loc: DefLoc) || |
712 | Name->getName() == "__GCC_HAVE_DWARF2_CFI_ASM" ) |
713 | return true; |
714 | |
715 | auto ID = getSymbolIDCached(MacroName: Name->getName(), MI, SM); |
716 | if (!ID) |
717 | return true; |
718 | |
719 | auto SpellingLoc = SM.getSpellingLoc(Loc); |
720 | bool IsMainFileOnly = |
721 | SM.isInMainFile(Loc: SM.getExpansionLoc(Loc: DefLoc)) && |
722 | !isHeaderFile(FileName: SM.getFileEntryRefForID(FID: SM.getMainFileID())->getName(), |
723 | LangOpts: ASTCtx->getLangOpts()); |
724 | // Do not store references to main-file macros. |
725 | if ((static_cast<unsigned>(Opts.RefFilter) & Roles) && !IsMainFileOnly && |
726 | (Opts.RefsInHeaders || SM.getFileID(SpellingLoc) == SM.getMainFileID())) { |
727 | // FIXME: Populate container information for macro references. |
728 | // FIXME: All MacroRefs are marked as Spelled now, but this should be |
729 | // checked. |
730 | addRef(ID, SR: SymbolRef{.Loc: Loc, .FID: SM.getFileID(SpellingLoc: Loc), .Roles: Roles, /*Container=*/nullptr, |
731 | /*Spelled=*/true}); |
732 | } |
733 | |
734 | // Collect symbols. |
735 | if (!Opts.CollectMacro) |
736 | return true; |
737 | |
738 | // Skip main-file macros if we are not collecting them. |
739 | if (IsMainFileOnly && !Opts.CollectMainFileSymbols) |
740 | return false; |
741 | |
742 | // Mark the macro as referenced if this is a reference coming from the main |
743 | // file. The macro may not be an interesting symbol, but it's cheaper to check |
744 | // at the end. |
745 | if (Opts.CountReferences && |
746 | (Roles & static_cast<unsigned>(index::SymbolRole::Reference)) && |
747 | SM.getFileID(SpellingLoc) == SM.getMainFileID()) |
748 | ReferencedSymbols.insert(V: ID); |
749 | |
750 | // Don't continue indexing if this is a mere reference. |
751 | // FIXME: remove macro with ID if it is undefined. |
752 | if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) || |
753 | Roles & static_cast<unsigned>(index::SymbolRole::Definition))) |
754 | return true; |
755 | |
756 | // Only collect one instance in case there are multiple. |
757 | if (Symbols.find(ID) != nullptr) |
758 | return true; |
759 | |
760 | Symbol S; |
761 | S.ID = std::move(ID); |
762 | S.Name = Name->getName(); |
763 | if (!IsMainFileOnly) { |
764 | S.Flags |= Symbol::IndexedForCodeCompletion; |
765 | S.Flags |= Symbol::VisibleOutsideFile; |
766 | } |
767 | S.SymInfo = index::getSymbolInfoForMacro(MI: *MI); |
768 | S.Origin = Opts.Origin; |
769 | // FIXME: use the result to filter out symbols. |
770 | shouldIndexFile(FID: SM.getFileID(SpellingLoc: Loc)); |
771 | if (auto DeclLoc = getTokenLocation(TokLoc: DefLoc)) |
772 | S.CanonicalDeclaration = *DeclLoc; |
773 | |
774 | CodeCompletionResult SymbolCompletion(Name); |
775 | const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro( |
776 | PP&: *PP, Allocator&: *CompletionAllocator, CCTUInfo&: *CompletionTUInfo); |
777 | std::string Signature; |
778 | std::string SnippetSuffix; |
779 | getSignature(CCS: *CCS, Signature: &Signature, Snippet: &SnippetSuffix, ResultKind: SymbolCompletion.Kind, |
780 | CursorKind: SymbolCompletion.CursorKind); |
781 | S.Signature = Signature; |
782 | S.CompletionSnippetSuffix = SnippetSuffix; |
783 | |
784 | IndexedMacros.insert(V: Name); |
785 | |
786 | setIncludeLocation(S, DefLoc, Sym: include_cleaner::Macro{.Name: Name, .Definition: DefLoc}); |
787 | Symbols.insert(S); |
788 | return true; |
789 | } |
790 | |
791 | void SymbolCollector::processRelations( |
792 | const NamedDecl &ND, const SymbolID &ID, |
793 | ArrayRef<index::SymbolRelation> Relations) { |
794 | for (const auto &R : Relations) { |
795 | auto RKind = indexableRelation(R); |
796 | if (!RKind) |
797 | continue; |
798 | const Decl *Object = R.RelatedSymbol; |
799 | |
800 | auto ObjectID = getSymbolIDCached(D: Object); |
801 | if (!ObjectID) |
802 | continue; |
803 | |
804 | // Record the relation. |
805 | // TODO: There may be cases where the object decl is not indexed for some |
806 | // reason. Those cases should probably be removed in due course, but for |
807 | // now there are two possible ways to handle it: |
808 | // (A) Avoid storing the relation in such cases. |
809 | // (B) Store it anyways. Clients will likely lookup() the SymbolID |
810 | // in the index and find nothing, but that's a situation they |
811 | // probably need to handle for other reasons anyways. |
812 | // We currently do (B) because it's simpler. |
813 | if (*RKind == RelationKind::BaseOf) |
814 | this->Relations.insert(R: {.Subject: ID, .Predicate: *RKind, .Object: ObjectID}); |
815 | else if (*RKind == RelationKind::OverriddenBy) |
816 | this->Relations.insert(R: {.Subject: ObjectID, .Predicate: *RKind, .Object: ID}); |
817 | } |
818 | } |
819 | |
820 | void SymbolCollector::setIncludeLocation(const Symbol &S, SourceLocation DefLoc, |
821 | const include_cleaner::Symbol &Sym) { |
822 | const auto &SM = PP->getSourceManager(); |
823 | if (!Opts.CollectIncludePath || |
824 | shouldCollectIncludePath(Kind: S.SymInfo.Kind) == Symbol::Invalid) |
825 | return; |
826 | |
827 | // Use the expansion location to get the #include header since this is |
828 | // where the symbol is exposed. |
829 | if (FileID FID = SM.getDecomposedExpansionLoc(Loc: DefLoc).first; FID.isValid()) |
830 | IncludeFiles[S.ID] = FID; |
831 | |
832 | // We update providers for a symbol with each occurence, as SymbolCollector |
833 | // might run while parsing, rather than at the end of a translation unit. |
834 | // Hence we see more and more redecls over time. |
835 | SymbolProviders[S.ID] = |
836 | include_cleaner::headersForSymbol(S: Sym, SM, PI: Opts.PragmaIncludes); |
837 | } |
838 | |
839 | llvm::StringRef (const Symbol *S, const LangOptions &LangOpts) { |
840 | tooling::stdlib::Lang Lang = tooling::stdlib::Lang::CXX; |
841 | if (LangOpts.C11) |
842 | Lang = tooling::stdlib::Lang::C; |
843 | else if(!LangOpts.CPlusPlus) |
844 | return "" ; |
845 | |
846 | if (S->Scope == "std::" && S->Name == "move" ) { |
847 | if (!S->Signature.contains(C: ',')) |
848 | return "<utility>" ; |
849 | return "<algorithm>" ; |
850 | } |
851 | |
852 | if (auto StdSym = tooling::stdlib::Symbol::named(Scope: S->Scope, Name: S->Name, Language: Lang)) |
853 | if (auto = StdSym->header()) |
854 | return Header->name(); |
855 | return "" ; |
856 | } |
857 | |
858 | void SymbolCollector::finish() { |
859 | // At the end of the TU, add 1 to the refcount of all referenced symbols. |
860 | for (const auto &ID : ReferencedSymbols) { |
861 | if (const auto *S = Symbols.find(ID)) { |
862 | // SymbolSlab::Builder returns const symbols because strings are interned |
863 | // and modifying returned symbols without inserting again wouldn't go |
864 | // well. const_cast is safe here as we're modifying a data owned by the |
865 | // Symbol. This reduces time spent in SymbolCollector by ~1%. |
866 | ++const_cast<Symbol *>(S)->References; |
867 | } |
868 | } |
869 | if (Opts.CollectMacro) { |
870 | assert(PP); |
871 | // First, drop header guards. We can't identify these until EOF. |
872 | for (const IdentifierInfo *II : IndexedMacros) { |
873 | if (const auto *MI = PP->getMacroDefinition(II).getMacroInfo()) |
874 | if (auto ID = |
875 | getSymbolIDCached(MacroName: II->getName(), MI, SM: PP->getSourceManager())) |
876 | if (MI->isUsedForHeaderGuard()) |
877 | Symbols.erase(ID); |
878 | } |
879 | } |
880 | llvm::DenseMap<FileID, bool> FileToContainsImportsOrObjC; |
881 | llvm::DenseMap<include_cleaner::Header, std::string> ; |
882 | // Fill in IncludeHeaders. |
883 | // We delay this until end of TU so header guards are all resolved. |
884 | for (const auto &[SID, Providers] : SymbolProviders) { |
885 | const Symbol *S = Symbols.find(ID: SID); |
886 | if (!S) |
887 | continue; |
888 | |
889 | FileID FID = IncludeFiles.lookup(Val: SID); |
890 | // Determine if the FID is #include'd or #import'ed. |
891 | Symbol::IncludeDirective Directives = Symbol::Invalid; |
892 | auto CollectDirectives = shouldCollectIncludePath(Kind: S->SymInfo.Kind); |
893 | if ((CollectDirectives & Symbol::Include) != 0) |
894 | Directives |= Symbol::Include; |
895 | // Only allow #import for symbols from ObjC-like files. |
896 | if ((CollectDirectives & Symbol::Import) != 0 && FID.isValid()) { |
897 | auto [It, Inserted] = FileToContainsImportsOrObjC.try_emplace(Key: FID); |
898 | if (Inserted) |
899 | It->second = FilesWithObjCConstructs.contains(V: FID) || |
900 | tooling::codeContainsImports( |
901 | Code: ASTCtx->getSourceManager().getBufferData(FID)); |
902 | if (It->second) |
903 | Directives |= Symbol::Import; |
904 | } |
905 | |
906 | if (Directives == Symbol::Invalid) |
907 | continue; |
908 | |
909 | // Use the include location-based logic for Objective-C symbols. |
910 | if (Directives & Symbol::Import) { |
911 | llvm::StringRef = getStdHeader(S, LangOpts: ASTCtx->getLangOpts()); |
912 | if (IncludeHeader.empty()) |
913 | IncludeHeader = HeaderFileURIs->getIncludeHeader(FID); |
914 | |
915 | if (!IncludeHeader.empty()) { |
916 | auto NewSym = *S; |
917 | NewSym.IncludeHeaders.push_back(Elt: {IncludeHeader, 1, Directives}); |
918 | Symbols.insert(S: NewSym); |
919 | } |
920 | // FIXME: use providers from include-cleaner library once it's polished |
921 | // for Objective-C. |
922 | continue; |
923 | } |
924 | |
925 | // For #include's, use the providers computed by the include-cleaner |
926 | // library. |
927 | assert(Directives == Symbol::Include); |
928 | // Ignore providers that are not self-contained, this is especially |
929 | // important for symbols defined in the main-file. We want to prefer the |
930 | // header, if possible. |
931 | // TODO: Limit this to specifically ignore main file, when we're indexing a |
932 | // non-header file? |
933 | auto SelfContainedProvider = |
934 | [this](llvm::ArrayRef<include_cleaner::Header> Providers) |
935 | -> std::optional<include_cleaner::Header> { |
936 | for (const auto &H : Providers) { |
937 | if (H.kind() != include_cleaner::Header::Physical) |
938 | return H; |
939 | if (tooling::isSelfContainedHeader(FE: H.physical(), SM: PP->getSourceManager(), |
940 | HeaderInfo: PP->getHeaderSearchInfo())) |
941 | return H; |
942 | } |
943 | return std::nullopt; |
944 | }; |
945 | const auto OptionalProvider = SelfContainedProvider(Providers); |
946 | if (!OptionalProvider) |
947 | continue; |
948 | const auto &H = *OptionalProvider; |
949 | const auto [SpellingIt, Inserted] = HeaderSpelling.try_emplace(Key: H); |
950 | if (Inserted) { |
951 | auto &SM = ASTCtx->getSourceManager(); |
952 | if (H.kind() == include_cleaner::Header::Kind::Physical) { |
953 | // FIXME: Get rid of this once include-cleaner has support for system |
954 | // headers. |
955 | if (auto Canonical = |
956 | HeaderFileURIs->mapCanonical(HeaderPath: H.physical().getName()); |
957 | !Canonical.empty()) |
958 | SpellingIt->second = Canonical; |
959 | // For physical files, prefer URIs as spellings might change |
960 | // depending on the translation unit. |
961 | else if (tooling::isSelfContainedHeader(FE: H.physical(), SM, |
962 | HeaderInfo: PP->getHeaderSearchInfo())) |
963 | SpellingIt->second = |
964 | HeaderFileURIs->toURI(FE: H.physical()); |
965 | } else { |
966 | SpellingIt->second = include_cleaner::spellHeader( |
967 | Input: {.H: H, .HS: PP->getHeaderSearchInfo(), |
968 | .Main: SM.getFileEntryForID(FID: SM.getMainFileID())}); |
969 | } |
970 | } |
971 | |
972 | if (!SpellingIt->second.empty()) { |
973 | auto NewSym = *S; |
974 | NewSym.IncludeHeaders.push_back(Elt: {SpellingIt->second, 1, Directives}); |
975 | Symbols.insert(S: NewSym); |
976 | } |
977 | } |
978 | |
979 | ReferencedSymbols.clear(); |
980 | IncludeFiles.clear(); |
981 | SymbolProviders.clear(); |
982 | FilesWithObjCConstructs.clear(); |
983 | } |
984 | |
985 | const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, SymbolID ID, |
986 | bool IsMainFileOnly) { |
987 | auto &Ctx = ND.getASTContext(); |
988 | auto &SM = Ctx.getSourceManager(); |
989 | |
990 | Symbol S; |
991 | S.ID = std::move(ID); |
992 | std::string QName = printQualifiedName(ND); |
993 | // FIXME: this returns foo:bar: for objective-C methods, we prefer only foo: |
994 | // for consistency with CodeCompletionString and a clean name/signature split. |
995 | std::tie(args&: S.Scope, args&: S.Name) = splitQualifiedName(QName); |
996 | std::string TemplateSpecializationArgs = printTemplateSpecializationArgs(ND); |
997 | S.TemplateSpecializationArgs = TemplateSpecializationArgs; |
998 | |
999 | // We collect main-file symbols, but do not use them for code completion. |
1000 | if (!IsMainFileOnly && isIndexedForCodeCompletion(ND, Ctx)) |
1001 | S.Flags |= Symbol::IndexedForCodeCompletion; |
1002 | if (isImplementationDetail(&ND)) |
1003 | S.Flags |= Symbol::ImplementationDetail; |
1004 | if (!IsMainFileOnly) |
1005 | S.Flags |= Symbol::VisibleOutsideFile; |
1006 | S.SymInfo = index::getSymbolInfo(&ND); |
1007 | auto Loc = nameLocation(ND, SM); |
1008 | assert(Loc.isValid() && "Invalid source location for NamedDecl" ); |
1009 | // FIXME: use the result to filter out symbols. |
1010 | auto FID = SM.getFileID(Loc); |
1011 | shouldIndexFile(FID: FID); |
1012 | if (auto DeclLoc = getTokenLocation(Loc)) |
1013 | S.CanonicalDeclaration = *DeclLoc; |
1014 | |
1015 | S.Origin = Opts.Origin; |
1016 | if (ND.getAvailability() == AR_Deprecated) |
1017 | S.Flags |= Symbol::Deprecated; |
1018 | |
1019 | // Add completion info. |
1020 | // FIXME: we may want to choose a different redecl, or combine from several. |
1021 | assert(ASTCtx && PP && "ASTContext and Preprocessor must be set." ); |
1022 | // We use the primary template, as clang does during code completion. |
1023 | CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0); |
1024 | const auto *CCS = SymbolCompletion.CreateCodeCompletionString( |
1025 | Ctx&: *ASTCtx, PP&: *PP, CCContext: CodeCompletionContext::CCC_Symbol, Allocator&: *CompletionAllocator, |
1026 | CCTUInfo&: *CompletionTUInfo, |
1027 | /*IncludeBriefComments*/ false); |
1028 | std::string Documentation = |
1029 | formatDocumentation(*CCS, getDocComment(Ctx, SymbolCompletion, |
1030 | /*CommentsFromHeaders=*/true)); |
1031 | if (!(S.Flags & Symbol::IndexedForCodeCompletion)) { |
1032 | if (Opts.StoreAllDocumentation) |
1033 | S.Documentation = Documentation; |
1034 | Symbols.insert(S); |
1035 | return Symbols.find(ID: S.ID); |
1036 | } |
1037 | S.Documentation = Documentation; |
1038 | std::string Signature; |
1039 | std::string SnippetSuffix; |
1040 | getSignature(CCS: *CCS, Signature: &Signature, Snippet: &SnippetSuffix, ResultKind: SymbolCompletion.Kind, |
1041 | CursorKind: SymbolCompletion.CursorKind); |
1042 | S.Signature = Signature; |
1043 | S.CompletionSnippetSuffix = SnippetSuffix; |
1044 | std::string ReturnType = getReturnType(CCS: *CCS); |
1045 | S.ReturnType = ReturnType; |
1046 | |
1047 | std::optional<OpaqueType> TypeStorage; |
1048 | if (S.Flags & Symbol::IndexedForCodeCompletion) { |
1049 | TypeStorage = OpaqueType::fromCompletionResult(Ctx&: *ASTCtx, R: SymbolCompletion); |
1050 | if (TypeStorage) |
1051 | S.Type = TypeStorage->raw(); |
1052 | } |
1053 | |
1054 | Symbols.insert(S); |
1055 | setIncludeLocation(S, DefLoc: ND.getLocation(), Sym: include_cleaner::Symbol{ND}); |
1056 | if (S.SymInfo.Lang == index::SymbolLanguage::ObjC) |
1057 | FilesWithObjCConstructs.insert(FID); |
1058 | return Symbols.find(ID: S.ID); |
1059 | } |
1060 | |
1061 | void SymbolCollector::addDefinition(const NamedDecl &ND, |
1062 | const Symbol &DeclSym) { |
1063 | if (DeclSym.Definition) |
1064 | return; |
1065 | const auto &SM = ND.getASTContext().getSourceManager(); |
1066 | auto Loc = nameLocation(ND, SM); |
1067 | shouldIndexFile(FID: SM.getFileID(Loc)); |
1068 | auto DefLoc = getTokenLocation(TokLoc: Loc); |
1069 | // If we saw some forward declaration, we end up copying the symbol. |
1070 | // This is not ideal, but avoids duplicating the "is this a definition" check |
1071 | // in clang::index. We should only see one definition. |
1072 | if (!DefLoc) |
1073 | return; |
1074 | Symbol S = DeclSym; |
1075 | // FIXME: use the result to filter out symbols. |
1076 | S.Definition = *DefLoc; |
1077 | Symbols.insert(S); |
1078 | } |
1079 | |
1080 | bool SymbolCollector::shouldIndexFile(FileID FID) { |
1081 | if (!Opts.FileFilter) |
1082 | return true; |
1083 | auto I = FilesToIndexCache.try_emplace(Key: FID); |
1084 | if (I.second) |
1085 | I.first->second = Opts.FileFilter(ASTCtx->getSourceManager(), FID); |
1086 | return I.first->second; |
1087 | } |
1088 | |
1089 | void SymbolCollector::addRef(SymbolID ID, const SymbolRef &SR) { |
1090 | const auto &SM = ASTCtx->getSourceManager(); |
1091 | // FIXME: use the result to filter out references. |
1092 | shouldIndexFile(FID: SR.FID); |
1093 | if (const auto FE = SM.getFileEntryRefForID(FID: SR.FID)) { |
1094 | auto Range = getTokenRange(TokLoc: SR.Loc, SM, LangOpts: ASTCtx->getLangOpts()); |
1095 | Ref R; |
1096 | R.Location.Start = Range.first; |
1097 | R.Location.End = Range.second; |
1098 | R.Location.FileURI = HeaderFileURIs->toURI(FE: *FE).c_str(); |
1099 | R.Kind = toRefKind(Roles: SR.Roles, Spelled: SR.Spelled); |
1100 | R.Container = getSymbolIDCached(D: SR.Container); |
1101 | Refs.insert(ID, S: R); |
1102 | } |
1103 | } |
1104 | |
1105 | SymbolID SymbolCollector::getSymbolIDCached(const Decl *D) { |
1106 | auto It = DeclToIDCache.try_emplace(Key: D, Args: SymbolID{}); |
1107 | if (It.second) |
1108 | It.first->second = getSymbolID(D); |
1109 | return It.first->second; |
1110 | } |
1111 | |
1112 | SymbolID SymbolCollector::getSymbolIDCached(const llvm::StringRef MacroName, |
1113 | const MacroInfo *MI, |
1114 | const SourceManager &SM) { |
1115 | auto It = MacroToIDCache.try_emplace(Key: MI, Args: SymbolID{}); |
1116 | if (It.second) |
1117 | It.first->second = getSymbolID(MacroName, MI, SM); |
1118 | return It.first->second; |
1119 | } |
1120 | } // namespace clangd |
1121 | } // namespace clang |
1122 | |