1//===- IndexingContext.cpp - Indexing context data ------------------------===//
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 "IndexingContext.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Attr.h"
12#include "clang/AST/DeclObjC.h"
13#include "clang/AST/DeclTemplate.h"
14#include "clang/Basic/SourceLocation.h"
15#include "clang/Basic/SourceManager.h"
16#include "clang/Index/IndexDataConsumer.h"
17
18using namespace clang;
19using namespace index;
20
21static bool isGeneratedDecl(const Decl *D) {
22  if (auto *attr = D->getAttr<ExternalSourceSymbolAttr>()) {
23    return attr->getGeneratedDeclaration();
24  }
25  return false;
26}
27
28bool IndexingContext::shouldIndex(const Decl *D) {
29  return !isGeneratedDecl(D);
30}
31
32const LangOptions &IndexingContext::getLangOpts() const {
33  return Ctx->getLangOpts();
34}
35
36bool IndexingContext::shouldIndexFunctionLocalSymbols() const {
37  return IndexOpts.IndexFunctionLocals;
38}
39
40bool IndexingContext::shouldIndexImplicitInstantiation() const {
41  return IndexOpts.IndexImplicitInstantiation;
42}
43
44bool IndexingContext::shouldIndexParametersInDeclarations() const {
45  return IndexOpts.IndexParametersInDeclarations;
46}
47
48bool IndexingContext::shouldIndexTemplateParameters() const {
49  return IndexOpts.IndexTemplateParameters;
50}
51
52bool IndexingContext::handleDecl(const Decl *D,
53                                 SymbolRoleSet Roles,
54                                 ArrayRef<SymbolRelation> Relations) {
55  return handleDecl(D, D->getLocation(), Roles, Relations);
56}
57
58bool IndexingContext::handleDecl(const Decl *D, SourceLocation Loc,
59                                 SymbolRoleSet Roles,
60                                 ArrayRef<SymbolRelation> Relations,
61                                 const DeclContext *DC) {
62  if (!DC)
63    DC = D->getDeclContext();
64
65  const Decl *OrigD = D;
66  if (isa<ObjCPropertyImplDecl>(D)) {
67    D = cast<ObjCPropertyImplDecl>(D)->getPropertyDecl();
68  }
69  return handleDeclOccurrence(D, Loc, /*IsRef=*/false, cast<Decl>(DC),
70                              Roles, Relations,
71                              nullptr, OrigD, DC);
72}
73
74bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc,
75                                      const NamedDecl *Parent,
76                                      const DeclContext *DC,
77                                      SymbolRoleSet Roles,
78                                      ArrayRef<SymbolRelation> Relations,
79                                      const Expr *RefE,
80                                      const Decl *RefD) {
81  if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalSymbol(D))
82    return true;
83
84  if (!shouldIndexTemplateParameters() &&
85      (isa<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) ||
86       isa<TemplateTemplateParmDecl>(D))) {
87    return true;
88  }
89  return handleDeclOccurrence(D, Loc, /*IsRef=*/true, Parent, Roles, Relations,
90                              RefE, RefD, DC);
91}
92
93static void reportModuleReferences(const Module *Mod,
94                                   ArrayRef<SourceLocation> IdLocs,
95                                   const ImportDecl *ImportD,
96                                   IndexDataConsumer &DataConsumer) {
97  if (!Mod)
98    return;
99  reportModuleReferences(Mod->Parent, IdLocs.drop_back(), ImportD,
100                         DataConsumer);
101  DataConsumer.handleModuleOccurrence(
102      ImportD, Mod, (SymbolRoleSet)SymbolRole::Reference, IdLocs.back());
103}
104
105bool IndexingContext::importedModule(const ImportDecl *ImportD) {
106  if (ImportD->isInvalidDecl())
107    return true;
108
109  SourceLocation Loc;
110  auto IdLocs = ImportD->getIdentifierLocs();
111  if (!IdLocs.empty())
112    Loc = IdLocs.back();
113  else
114    Loc = ImportD->getLocation();
115
116  SourceManager &SM = Ctx->getSourceManager();
117  FileID FID = SM.getFileID(SM.getFileLoc(Loc));
118  if (FID.isInvalid())
119    return true;
120
121  bool Invalid = false;
122  const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);
123  if (Invalid || !SEntry.isFile())
124    return true;
125
126  if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) {
127    switch (IndexOpts.SystemSymbolFilter) {
128    case IndexingOptions::SystemSymbolFilterKind::None:
129      return true;
130    case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly:
131    case IndexingOptions::SystemSymbolFilterKind::All:
132      break;
133    }
134  }
135
136  const Module *Mod = ImportD->getImportedModule();
137  if (!ImportD->isImplicit() && Mod->Parent && !IdLocs.empty()) {
138    reportModuleReferences(Mod->Parent, IdLocs.drop_back(), ImportD,
139                           DataConsumer);
140  }
141
142  SymbolRoleSet Roles = (unsigned)SymbolRole::Declaration;
143  if (ImportD->isImplicit())
144    Roles |= (unsigned)SymbolRole::Implicit;
145
146  return DataConsumer.handleModuleOccurrence(ImportD, Mod, Roles, Loc);
147}
148
149bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) {
150  TemplateSpecializationKind TKind = TSK_Undeclared;
151  if (const ClassTemplateSpecializationDecl *
152      SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
153    TKind = SD->getSpecializationKind();
154  } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
155    TKind = FD->getTemplateSpecializationKind();
156  } else if (auto *VD = dyn_cast<VarDecl>(D)) {
157    TKind = VD->getTemplateSpecializationKind();
158  } else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
159    if (RD->getInstantiatedFromMemberClass())
160      TKind = RD->getTemplateSpecializationKind();
161  } else if (const auto *ED = dyn_cast<EnumDecl>(D)) {
162    if (ED->getInstantiatedFromMemberEnum())
163      TKind = ED->getTemplateSpecializationKind();
164  } else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D) ||
165             isa<EnumConstantDecl>(D)) {
166    if (const auto *Parent = dyn_cast<Decl>(D->getDeclContext()))
167      return isTemplateImplicitInstantiation(Parent);
168  }
169  switch (TKind) {
170    case TSK_Undeclared:
171      // Instantiation maybe not happen yet when we see a SpecializationDecl,
172      // e.g. when the type doesn't need to be complete, we still treat it as an
173      // instantiation as we'd like to keep the canonicalized result consistent.
174      return isa<ClassTemplateSpecializationDecl>(D);
175    case TSK_ExplicitSpecialization:
176      return false;
177    case TSK_ImplicitInstantiation:
178    case TSK_ExplicitInstantiationDeclaration:
179    case TSK_ExplicitInstantiationDefinition:
180      return true;
181  }
182  llvm_unreachable("invalid TemplateSpecializationKind");
183}
184
185bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) {
186  if (isa<ObjCInterfaceDecl>(D))
187    return false;
188  if (isa<ObjCCategoryDecl>(D))
189    return false;
190  if (isa<ObjCIvarDecl>(D))
191    return false;
192  if (isa<ObjCMethodDecl>(D))
193    return false;
194  if (isa<ImportDecl>(D))
195    return false;
196  return true;
197}
198
199static const CXXRecordDecl *
200getDeclContextForTemplateInstationPattern(const Decl *D) {
201  if (const auto *CTSD =
202          dyn_cast<ClassTemplateSpecializationDecl>(D->getDeclContext()))
203    return CTSD->getTemplateInstantiationPattern();
204  else if (const auto *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext()))
205    return RD->getInstantiatedFromMemberClass();
206  return nullptr;
207}
208
209static const Decl *adjustTemplateImplicitInstantiation(const Decl *D) {
210  if (const ClassTemplateSpecializationDecl *
211      SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
212    const auto *Template = SD->getTemplateInstantiationPattern();
213    if (Template)
214      return Template;
215    // Fallback to primary template if no instantiation is available yet (e.g.
216    // the type doesn't need to be complete).
217    return SD->getSpecializedTemplate()->getTemplatedDecl();
218  } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
219    return FD->getTemplateInstantiationPattern();
220  } else if (auto *VD = dyn_cast<VarDecl>(D)) {
221    return VD->getTemplateInstantiationPattern();
222  } else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
223    return RD->getInstantiatedFromMemberClass();
224  } else if (const auto *ED = dyn_cast<EnumDecl>(D)) {
225    return ED->getInstantiatedFromMemberEnum();
226  } else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D)) {
227    const auto *ND = cast<NamedDecl>(D);
228    if (const CXXRecordDecl *Pattern =
229            getDeclContextForTemplateInstationPattern(ND)) {
230      for (const NamedDecl *BaseND : Pattern->lookup(ND->getDeclName())) {
231        if (BaseND->isImplicit())
232          continue;
233        if (BaseND->getKind() == ND->getKind())
234          return BaseND;
235      }
236    }
237  } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) {
238    if (const auto *ED = dyn_cast<EnumDecl>(ECD->getDeclContext())) {
239      if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) {
240        for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName()))
241          return BaseECD;
242      }
243    }
244  }
245  return nullptr;
246}
247
248static bool isDeclADefinition(const Decl *D, const DeclContext *ContainerDC, ASTContext &Ctx) {
249  if (auto VD = dyn_cast<VarDecl>(D))
250    return VD->isThisDeclarationADefinition(Ctx);
251
252  if (auto FD = dyn_cast<FunctionDecl>(D))
253    return FD->isThisDeclarationADefinition();
254
255  if (auto TD = dyn_cast<TagDecl>(D))
256    return TD->isThisDeclarationADefinition();
257
258  if (auto MD = dyn_cast<ObjCMethodDecl>(D))
259    return MD->isThisDeclarationADefinition() || isa<ObjCImplDecl>(ContainerDC);
260
261  if (isa<TypedefNameDecl>(D) || isa<EnumConstantDecl>(D) ||
262      isa<FieldDecl>(D) || isa<MSPropertyDecl>(D) || isa<ObjCImplDecl>(D) ||
263      isa<ObjCPropertyImplDecl>(D) || isa<ConceptDecl>(D))
264    return true;
265
266  return false;
267}
268
269/// Whether the given NamedDecl should be skipped because it has no name.
270static bool shouldSkipNamelessDecl(const NamedDecl *ND) {
271  return (ND->getDeclName().isEmpty() && !isa<TagDecl>(ND) &&
272          !isa<ObjCCategoryDecl>(ND)) || isa<CXXDeductionGuideDecl>(ND);
273}
274
275static const Decl *adjustParent(const Decl *Parent) {
276  if (!Parent)
277    return nullptr;
278  for (;; Parent = cast<Decl>(Parent->getDeclContext())) {
279    if (isa<TranslationUnitDecl>(Parent))
280      return nullptr;
281    if (isa<LinkageSpecDecl>(Parent) || isa<BlockDecl>(Parent))
282      continue;
283    if (auto NS = dyn_cast<NamespaceDecl>(Parent)) {
284      if (NS->isAnonymousNamespace())
285        continue;
286    } else if (auto RD = dyn_cast<RecordDecl>(Parent)) {
287      if (RD->isAnonymousStructOrUnion())
288        continue;
289    } else if (auto ND = dyn_cast<NamedDecl>(Parent)) {
290      if (shouldSkipNamelessDecl(ND))
291        continue;
292    }
293    return Parent;
294  }
295}
296
297static const Decl *getCanonicalDecl(const Decl *D) {
298  D = D->getCanonicalDecl();
299  if (auto TD = dyn_cast<TemplateDecl>(D)) {
300    if (auto TTD = TD->getTemplatedDecl()) {
301      D = TTD;
302      assert(D->isCanonicalDecl());
303    }
304  }
305
306  return D;
307}
308
309static bool shouldReportOccurrenceForSystemDeclOnlyMode(
310    bool IsRef, SymbolRoleSet Roles, ArrayRef<SymbolRelation> Relations) {
311  if (!IsRef)
312    return true;
313
314  auto acceptForRelation = [](SymbolRoleSet roles) -> bool {
315    bool accept = false;
316    applyForEachSymbolRoleInterruptible(roles, [&accept](SymbolRole r) -> bool {
317      switch (r) {
318      case SymbolRole::RelationChildOf:
319      case SymbolRole::RelationBaseOf:
320      case SymbolRole::RelationOverrideOf:
321      case SymbolRole::RelationExtendedBy:
322      case SymbolRole::RelationAccessorOf:
323      case SymbolRole::RelationIBTypeOf:
324        accept = true;
325        return false;
326      case SymbolRole::Declaration:
327      case SymbolRole::Definition:
328      case SymbolRole::Reference:
329      case SymbolRole::Read:
330      case SymbolRole::Write:
331      case SymbolRole::Call:
332      case SymbolRole::Dynamic:
333      case SymbolRole::AddressOf:
334      case SymbolRole::Implicit:
335      case SymbolRole::Undefinition:
336      case SymbolRole::RelationReceivedBy:
337      case SymbolRole::RelationCalledBy:
338      case SymbolRole::RelationContainedBy:
339      case SymbolRole::RelationSpecializationOf:
340      case SymbolRole::NameReference:
341        return true;
342      }
343      llvm_unreachable("Unsupported SymbolRole value!");
344    });
345    return accept;
346  };
347
348  for (auto &Rel : Relations) {
349    if (acceptForRelation(Rel.Roles))
350      return true;
351  }
352
353  return false;
354}
355
356bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc,
357                                           bool IsRef, const Decl *Parent,
358                                           SymbolRoleSet Roles,
359                                           ArrayRef<SymbolRelation> Relations,
360                                           const Expr *OrigE,
361                                           const Decl *OrigD,
362                                           const DeclContext *ContainerDC) {
363  if (D->isImplicit() && !isa<ObjCMethodDecl>(D))
364    return true;
365  if (!isa<NamedDecl>(D) || shouldSkipNamelessDecl(cast<NamedDecl>(D)))
366    return true;
367
368  SourceManager &SM = Ctx->getSourceManager();
369  FileID FID = SM.getFileID(SM.getFileLoc(Loc));
370  if (FID.isInvalid())
371    return true;
372
373  bool Invalid = false;
374  const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);
375  if (Invalid || !SEntry.isFile())
376    return true;
377
378  if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) {
379    switch (IndexOpts.SystemSymbolFilter) {
380    case IndexingOptions::SystemSymbolFilterKind::None:
381      return true;
382    case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly:
383      if (!shouldReportOccurrenceForSystemDeclOnlyMode(IsRef, Roles, Relations))
384        return true;
385      break;
386    case IndexingOptions::SystemSymbolFilterKind::All:
387      break;
388    }
389  }
390
391  if (!OrigD)
392    OrigD = D;
393
394  if (isTemplateImplicitInstantiation(D)) {
395    if (!IsRef)
396      return true;
397    D = adjustTemplateImplicitInstantiation(D);
398    if (!D)
399      return true;
400    assert(!isTemplateImplicitInstantiation(D));
401  }
402
403  if (IsRef)
404    Roles |= (unsigned)SymbolRole::Reference;
405  else if (isDeclADefinition(OrigD, ContainerDC, *Ctx))
406    Roles |= (unsigned)SymbolRole::Definition;
407  else
408    Roles |= (unsigned)SymbolRole::Declaration;
409
410  D = getCanonicalDecl(D);
411  Parent = adjustParent(Parent);
412  if (Parent)
413    Parent = getCanonicalDecl(Parent);
414
415  SmallVector<SymbolRelation, 6> FinalRelations;
416  FinalRelations.reserve(Relations.size()+1);
417
418  auto addRelation = [&](SymbolRelation Rel) {
419    auto It = llvm::find_if(FinalRelations, [&](SymbolRelation Elem) -> bool {
420      return Elem.RelatedSymbol == Rel.RelatedSymbol;
421    });
422    if (It != FinalRelations.end()) {
423      It->Roles |= Rel.Roles;
424    } else {
425      FinalRelations.push_back(Rel);
426    }
427    Roles |= Rel.Roles;
428  };
429
430  if (Parent) {
431    if (IsRef || (!isa<ParmVarDecl>(D) && isFunctionLocalSymbol(D))) {
432      addRelation(SymbolRelation{
433        (unsigned)SymbolRole::RelationContainedBy,
434        Parent
435      });
436    } else {
437      addRelation(SymbolRelation{
438        (unsigned)SymbolRole::RelationChildOf,
439        Parent
440      });
441    }
442  }
443
444  for (auto &Rel : Relations) {
445    addRelation(SymbolRelation(Rel.Roles,
446                               Rel.RelatedSymbol->getCanonicalDecl()));
447  }
448
449  IndexDataConsumer::ASTNodeInfo Node{OrigE, OrigD, Parent, ContainerDC};
450  return DataConsumer.handleDeclOccurrence(D, Roles, FinalRelations, Loc, Node);
451}
452
453void IndexingContext::handleMacroDefined(const IdentifierInfo &Name,
454                                         SourceLocation Loc,
455                                         const MacroInfo &MI) {
456  if (!shouldIndexMacroOccurrence(/*IsRef=*/false, Loc))
457    return;
458  SymbolRoleSet Roles = (unsigned)SymbolRole::Definition;
459  DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc);
460}
461
462void IndexingContext::handleMacroUndefined(const IdentifierInfo &Name,
463                                           SourceLocation Loc,
464                                           const MacroInfo &MI) {
465  if (!shouldIndexMacroOccurrence(/*IsRef=*/false, Loc))
466    return;
467  SymbolRoleSet Roles = (unsigned)SymbolRole::Undefinition;
468  DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc);
469}
470
471void IndexingContext::handleMacroReference(const IdentifierInfo &Name,
472                                           SourceLocation Loc,
473                                           const MacroInfo &MI) {
474  if (!shouldIndexMacroOccurrence(/*IsRef=*/true, Loc))
475    return;
476  SymbolRoleSet Roles = (unsigned)SymbolRole::Reference;
477  DataConsumer.handleMacroOccurrence(&Name, &MI, Roles, Loc);
478}
479
480bool IndexingContext::shouldIndexMacroOccurrence(bool IsRef,
481                                                 SourceLocation Loc) {
482  if (!IndexOpts.IndexMacros)
483    return false;
484
485  switch (IndexOpts.SystemSymbolFilter) {
486  case IndexingOptions::SystemSymbolFilterKind::None:
487    break;
488  case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly:
489    if (!IsRef)
490      return true;
491    break;
492  case IndexingOptions::SystemSymbolFilterKind::All:
493    return true;
494  }
495
496  SourceManager &SM = Ctx->getSourceManager();
497  FileID FID = SM.getFileID(SM.getFileLoc(Loc));
498  if (FID.isInvalid())
499    return false;
500
501  bool Invalid = false;
502  const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);
503  if (Invalid || !SEntry.isFile())
504    return false;
505
506  return SEntry.getFile().getFileCharacteristic() == SrcMgr::C_User;
507}
508