llvm-project

Форк
0
1121 строка · 43.2 Кб
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('_'))
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(Name, 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(TokLoc, SM, LangOpts);
130
  return {CreatePosition(TokLoc),
131
          CreatePosition(TokLoc.getLocWithOffset(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>(ND))
142
    return (Roles & static_cast<unsigned>(index::SymbolRole::Definition)) &&
143
           !isInsideMainFile(ND.getLocation(), SM);
144
  if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(&ND))
145
    return ID->isThisDeclarationADefinition();
146
  if (const auto *PD = dyn_cast<ObjCProtocolDecl>(&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, Tok, SM, LO))
187
    return false;
188
  auto TokSpelling = clang::Lexer::getSpelling(Tok, SM, LO);
189
  if (const auto *MD = dyn_cast<ObjCMethodDecl>(&ND))
190
    return TokSpelling == MD->getSelector().getNameForSlot(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::HeaderFileURICache {
199
  struct FrameworkUmbrellaSpelling {
200
    // Spelling for the public umbrella header, e.g. <Foundation/Foundation.h>
201
    std::optional<std::string> PublicHeader;
202
    // Spelling for the private umbrella header, e.g.
203
    // <Foundation/Foundation_Private.h>
204
    std::optional<std::string> PrivateHeader;
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 *&PP;
210
  const SourceManager &SM;
211
  const include_cleaner::PragmaIncludes *PI;
212
  llvm::StringRef FallbackDir;
213
  llvm::DenseMap<const FileEntry *, const std::string *> CacheFEToURI;
214
  llvm::StringMap<std::string> CachePathToURI;
215
  llvm::DenseMap<FileID, llvm::StringRef> CacheFIDToInclude;
216
  llvm::StringMap<std::string> CachePathToFrameworkSpelling;
217
  llvm::StringMap<FrameworkUmbrellaSpelling>
218
      CacheFrameworkToUmbrellaHeaderSpelling;
219

220
public:
221
  HeaderFileURICache(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 &toURI(const FileEntryRef FE) {
229
    auto R = CacheFEToURI.try_emplace(FE);
230
    if (R.second) {
231
      auto CanonPath = getCanonicalPath(FE, SM.getFileManager());
232
      R.first->second = &toURIInternal(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 &toURI(llvm::StringRef Path) {
241
    if (auto File = SM.getFileManager().getFileRef(Path))
242
      return toURI(*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 getIncludeHeader(FileID FID) {
251
    auto R = CacheFIDToInclude.try_emplace(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 mapCanonical(llvm::StringRef HeaderPath) {
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 SysHeaderMapping;
265
    SysHeaderMapping.addSystemHeadersMapping(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 &toURIInternal(llvm::StringRef Path) {
278
    auto R = CachePathToURI.try_emplace(Path);
279
    if (R.second) {
280
      llvm::SmallString<256> AbsPath = Path;
281
      if (!llvm::sys::path::is_absolute(AbsPath) && !FallbackDir.empty())
282
        llvm::sys::fs::make_absolute(FallbackDir, 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(AbsPath, /*remove_dot_dot=*/true);
287
      R.first->second = URI::create(AbsPath).toString();
288
    }
289
    return R.first->second;
290
  }
291

292
  struct FrameworkHeaderPath {
293
    // Path to the framework directory containing the Headers/PrivateHeaders
294
    // directories  e.g. /Frameworks/Foundation.framework/
295
    llvm::StringRef HeadersParentDir;
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 HeaderSubpath;
299
    // Whether this header is under the PrivateHeaders dir
300
    bool IsPrivateHeader;
301
  };
302

303
  std::optional<FrameworkHeaderPath>
304
  splitFrameworkHeaderPath(llvm::StringRef Path) {
305
    using namespace llvm::sys;
306
    path::reverse_iterator I = path::rbegin(Path);
307
    path::reverse_iterator Prev = I;
308
    path::reverse_iterator E = path::rend(Path);
309
    while (I != E) {
310
      if (*I == "Headers") {
311
        FrameworkHeaderPath HeaderPath;
312
        HeaderPath.HeadersParentDir = Path.substr(0, I - E);
313
        HeaderPath.HeaderSubpath = Path.substr(Prev - E);
314
        HeaderPath.IsPrivateHeader = false;
315
        return HeaderPath;
316
      }
317
      if (*I == "PrivateHeaders") {
318
        FrameworkHeaderPath HeaderPath;
319
        HeaderPath.HeadersParentDir = Path.substr(0, I - E);
320
        HeaderPath.HeaderSubpath = Path.substr(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
  getFrameworkUmbrellaSpelling(llvm::StringRef Framework,
338
                               const HeaderSearch &HS,
339
                               FrameworkHeaderPath &HeaderPath) {
340
    auto Res = CacheFrameworkToUmbrellaHeaderSpelling.try_emplace(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(UmbrellaPath, "Headers", Framework + ".h");
348

349
    llvm::vfs::Status Status;
350
    auto StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
351
    if (!StatErr)
352
      CachedSpelling->PublicHeader = llvm::formatv("<{0}/{0}.h>", Framework);
353

354
    UmbrellaPath = HeaderPath.HeadersParentDir;
355
    llvm::sys::path::append(UmbrellaPath, "PrivateHeaders",
356
                            Framework + "_Private.h");
357

358
    StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status);
359
    if (!StatErr)
360
      CachedSpelling->PrivateHeader =
361
          llvm::formatv("<{0}/{0}_Private.h>", 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
  getFrameworkHeaderIncludeSpelling(FileEntryRef FE, llvm::StringRef Framework,
373
                                    HeaderSearch &HS) {
374
    auto Res = CachePathToFrameworkSpelling.try_emplace(FE.getName());
375
    auto *CachedHeaderSpelling = &Res.first->second;
376
    if (!Res.second)
377
      return llvm::StringRef(*CachedHeaderSpelling);
378

379
    auto HeaderPath = splitFrameworkHeaderPath(FE.getName());
380
    if (!HeaderPath) {
381
      // Unexpected: must not be a proper framework header, don't cache the
382
      // failure.
383
      CachePathToFrameworkSpelling.erase(Res.first);
384
      return std::nullopt;
385
    }
386
    if (auto UmbrellaSpelling =
387
            getFrameworkUmbrellaSpelling(Framework, HS, *HeaderPath)) {
388
      *CachedHeaderSpelling = *UmbrellaSpelling;
389
      return llvm::StringRef(*CachedHeaderSpelling);
390
    }
391

392
    *CachedHeaderSpelling =
393
        llvm::formatv("<{0}/{1}>", Framework, HeaderPath->HeaderSubpath).str();
394
    return llvm::StringRef(*CachedHeaderSpelling);
395
  }
396

397
  llvm::StringRef getIncludeHeaderUncached(FileID FID) {
398
    const auto FE = SM.getFileEntryRefForID(FID);
399
    if (!FE || FE->getName().empty())
400
      return "";
401

402
    if (auto Verbatim = PI->getPublic(*FE); !Verbatim.empty())
403
      return Verbatim;
404

405
    llvm::StringRef Filename = FE->getName();
406
    if (auto Canonical = mapCanonical(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))
413
      if (!HFI->Framework.empty())
414
        if (auto Spelling =
415
                getFrameworkHeaderIncludeSpelling(*FE, HFI->Framework, HS))
416
          return *Spelling;
417

418
    if (!tooling::isSelfContainedHeader(*FE, PP->getSourceManager(),
419
                                        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(".inc") || Filename.ends_with(".def"))
423
        // Don't use cache reentrantly due to iterator invalidation.
424
        return getIncludeHeaderUncached(SM.getFileID(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);
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(SM.getFileID(TokLoc));
438
  if (!FE)
439
    return std::nullopt;
440

441
  SymbolLocation Result;
442
  Result.FileURI = HeaderFileURIs->toURI(*FE).c_str();
443
  auto Range = getTokenRange(TokLoc, SM, 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
      this->PP, ASTCtx->getSourceManager(), Opts);
457
  CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
458
  CompletionTUInfo =
459
      std::make_unique<CodeCompletionTUInfo>(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>(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(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>(Enclosing);
522
    if (ND && shouldCollectSymbol(*ND, ND->getASTContext(), Opts, true)) {
523
      break;
524
    }
525
    Enclosing = dyn_cast_or_null<Decl>(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(D, 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>(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>(D)) {
570
    DeclIsCanonical = true;
571
    if (const auto *CD = CID->getCategoryDecl())
572
      D = CD;
573
  }
574
  const NamedDecl *ND = dyn_cast<NamedDecl>(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(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(SM.getExpansionLoc(ND->getBeginLoc())) &&
595
      !isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
596
                    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, *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, 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>(ND)) {
617
    auto FileLoc = SM.getFileLoc(Loc);
618
    auto FID = SM.getFileID(FileLoc);
619
    if (Opts.RefsInHeaders || FID == SM.getMainFileID()) {
620
      addRef(ID, SymbolRef{FileLoc, FID, Roles,
621
                           getRefContainer(ASTNode.Parent, Opts),
622
                           isSpelled(FileLoc, *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>(ASTNode.OrigD);
634
  if (!OriginalDecl)
635
    return true;
636

637
  const Symbol *BasicSymbol = Symbols.find(ID);
638
  if (isPreferredDeclaration(*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), IsMainFileOnly);
644
  else if (!BasicSymbol || DeclIsCanonical)
645
    BasicSymbol = addDeclaration(*ND, std::move(ID), IsMainFileOnly);
646

647
  if (Roles & static_cast<unsigned>(index::SymbolRole::Definition))
648
    addDefinition(*OriginalDecl, *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(SM.getMainFileID());
657
  assert(MainFileEntryRef);
658

659
  const std::string &MainFileURI = HeaderFileURIs->toURI(*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(IDToRefs.first, R);
673
      if (IsDefinition) {
674
        Symbol S;
675
        S.ID = IDToRefs.first;
676
        auto StartLoc = cantFail(sourceLocationInMainFile(SM, Range.start));
677
        auto EndLoc = cantFail(sourceLocationInMainFile(SM, Range.end));
678
        S.Name = toSourceCode(SM, 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(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(DefLoc) ||
711
      SM.isWrittenInCommandLineFile(DefLoc) ||
712
      Name->getName() == "__GCC_HAVE_DWARF2_CFI_ASM")
713
    return true;
714

715
  auto ID = getSymbolIDCached(Name->getName(), MI, SM);
716
  if (!ID)
717
    return true;
718

719
  auto SpellingLoc = SM.getSpellingLoc(Loc);
720
  bool IsMainFileOnly =
721
      SM.isInMainFile(SM.getExpansionLoc(DefLoc)) &&
722
      !isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
723
                    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, SymbolRef{Loc, SM.getFileID(Loc), 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(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);
768
  S.Origin = Opts.Origin;
769
  // FIXME: use the result to filter out symbols.
770
  shouldIndexFile(SM.getFileID(Loc));
771
  if (auto DeclLoc = getTokenLocation(DefLoc))
772
    S.CanonicalDeclaration = *DeclLoc;
773

774
  CodeCompletionResult SymbolCompletion(Name);
775
  const auto *CCS = SymbolCompletion.CreateCodeCompletionStringForMacro(
776
      *PP, *CompletionAllocator, *CompletionTUInfo);
777
  std::string Signature;
778
  std::string SnippetSuffix;
779
  getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
780
               SymbolCompletion.CursorKind);
781
  S.Signature = Signature;
782
  S.CompletionSnippetSuffix = SnippetSuffix;
783

784
  IndexedMacros.insert(Name);
785

786
  setIncludeLocation(S, DefLoc, include_cleaner::Macro{Name, 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(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({ID, *RKind, ObjectID});
815
    else if (*RKind == RelationKind::OverriddenBy)
816
      this->Relations.insert({ObjectID, *RKind, 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(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(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(Sym, SM, Opts.PragmaIncludes);
837
}
838

839
llvm::StringRef getStdHeader(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(','))
848
      return "<utility>";
849
    return "<algorithm>";
850
  }
851

852
  if (auto StdSym = tooling::stdlib::Symbol::named(S->Scope, S->Name, Lang))
853
    if (auto Header = 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(II->getName(), MI, 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> HeaderSpelling;
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(SID);
886
    if (!S)
887
      continue;
888

889
    FileID FID = IncludeFiles.lookup(SID);
890
    // Determine if the FID is #include'd or #import'ed.
891
    Symbol::IncludeDirective Directives = Symbol::Invalid;
892
    auto CollectDirectives = shouldCollectIncludePath(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(FID);
898
      if (Inserted)
899
        It->second = FilesWithObjCConstructs.contains(FID) ||
900
                     tooling::codeContainsImports(
901
                         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 IncludeHeader = getStdHeader(S, 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({IncludeHeader, 1, Directives});
918
        Symbols.insert(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(H.physical(), PP->getSourceManager(),
940
                                           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(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(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(H.physical(), SM,
962
                                                PP->getHeaderSearchInfo()))
963
          SpellingIt->second =
964
              HeaderFileURIs->toURI(H.physical());
965
      } else {
966
        SpellingIt->second = include_cleaner::spellHeader(
967
            {H, PP->getHeaderSearchInfo(),
968
             SM.getFileEntryForID(SM.getMainFileID())});
969
      }
970
    }
971

972
    if (!SpellingIt->second.empty()) {
973
      auto NewSym = *S;
974
      NewSym.IncludeHeaders.push_back({SpellingIt->second, 1, Directives});
975
      Symbols.insert(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(S.Scope, 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);
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
      *ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1026
      *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(S.ID);
1036
  }
1037
  S.Documentation = Documentation;
1038
  std::string Signature;
1039
  std::string SnippetSuffix;
1040
  getSignature(*CCS, &Signature, &SnippetSuffix, SymbolCompletion.Kind,
1041
               SymbolCompletion.CursorKind);
1042
  S.Signature = Signature;
1043
  S.CompletionSnippetSuffix = SnippetSuffix;
1044
  std::string ReturnType = getReturnType(*CCS);
1045
  S.ReturnType = ReturnType;
1046

1047
  std::optional<OpaqueType> TypeStorage;
1048
  if (S.Flags & Symbol::IndexedForCodeCompletion) {
1049
    TypeStorage = OpaqueType::fromCompletionResult(*ASTCtx, SymbolCompletion);
1050
    if (TypeStorage)
1051
      S.Type = TypeStorage->raw();
1052
  }
1053

1054
  Symbols.insert(S);
1055
  setIncludeLocation(S, ND.getLocation(), include_cleaner::Symbol{ND});
1056
  if (S.SymInfo.Lang == index::SymbolLanguage::ObjC)
1057
    FilesWithObjCConstructs.insert(FID);
1058
  return Symbols.find(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(SM.getFileID(Loc));
1068
  auto DefLoc = getTokenLocation(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(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(SR.FID);
1093
  if (const auto FE = SM.getFileEntryRefForID(SR.FID)) {
1094
    auto Range = getTokenRange(SR.Loc, SM, ASTCtx->getLangOpts());
1095
    Ref R;
1096
    R.Location.Start = Range.first;
1097
    R.Location.End = Range.second;
1098
    R.Location.FileURI = HeaderFileURIs->toURI(*FE).c_str();
1099
    R.Kind = toRefKind(SR.Roles, SR.Spelled);
1100
    R.Container = getSymbolIDCached(SR.Container);
1101
    Refs.insert(ID, R);
1102
  }
1103
}
1104

1105
SymbolID SymbolCollector::getSymbolIDCached(const Decl *D) {
1106
  auto It = DeclToIDCache.try_emplace(D, 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(MI, 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

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.