1//===- ExtractAPI/ExtractAPIVisitor.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/// \file
10/// This file implements the ExtractAPIVisitor an ASTVisitor to collect API
11/// information.
12///
13//===----------------------------------------------------------------------===//
14
15#include "clang/ExtractAPI/ExtractAPIVisitor.h"
16
17#include "TypedefUnderlyingTypeResolver.h"
18#include "clang/AST/ASTConsumer.h"
19#include "clang/AST/ASTContext.h"
20#include "clang/AST/Decl.h"
21#include "clang/AST/DeclCXX.h"
22#include "clang/AST/ParentMapContext.h"
23#include "clang/AST/RawCommentList.h"
24#include "clang/Basic/SourceLocation.h"
25#include "clang/Basic/SourceManager.h"
26#include "clang/Basic/TargetInfo.h"
27#include "clang/ExtractAPI/API.h"
28#include "clang/ExtractAPI/AvailabilityInfo.h"
29#include "clang/ExtractAPI/DeclarationFragments.h"
30#include "clang/Frontend/ASTConsumers.h"
31#include "clang/Frontend/FrontendOptions.h"
32#include "llvm/Support/raw_ostream.h"
33
34using namespace clang;
35using namespace extractapi;
36
37namespace {
38
39StringRef getTypedefName(const TagDecl *Decl) {
40  if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
41    return TypedefDecl->getName();
42
43  return {};
44}
45
46template <class DeclTy>
47bool isInSystemHeader(const ASTContext &Context, const DeclTy *D) {
48  return Context.getSourceManager().isInSystemHeader(D->getLocation());
49}
50
51} // namespace
52
53bool ExtractAPIVisitor::VisitVarDecl(const VarDecl *Decl) {
54  // skip function parameters.
55  if (isa<ParmVarDecl>(Decl))
56    return true;
57
58  // Skip non-global variables in records (struct/union/class).
59  if (Decl->getDeclContext()->isRecord())
60    return true;
61
62  // Skip local variables inside function or method.
63  if (!Decl->isDefinedOutsideFunctionOrMethod())
64    return true;
65
66  // If this is a template but not specialization or instantiation, skip.
67  if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
68      Decl->getTemplateSpecializationKind() == TSK_Undeclared)
69    return true;
70
71  if (!LocationChecker(Decl->getLocation()))
72    return true;
73
74  // Collect symbol information.
75  StringRef Name = Decl->getName();
76  StringRef USR = API.recordUSR(Decl);
77  PresumedLoc Loc =
78      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
79  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
80  DocComment Comment;
81  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
82    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
83                                            Context.getDiagnostics());
84
85  // Build declaration fragments and sub-heading for the variable.
86  DeclarationFragments Declaration =
87      DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
88  DeclarationFragments SubHeading =
89      DeclarationFragmentsBuilder::getSubHeading(Decl);
90
91  // Add the global variable record to the API set.
92  API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
93                   Declaration, SubHeading, isInSystemHeader(Context, Decl));
94  return true;
95}
96
97bool ExtractAPIVisitor::VisitFunctionDecl(const FunctionDecl *Decl) {
98  if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
99    // Skip member function in class templates.
100    if (Method->getParent()->getDescribedClassTemplate() != nullptr)
101      return true;
102
103    // Skip methods in records.
104    for (auto P : Context.getParents(*Method)) {
105      if (P.get<CXXRecordDecl>())
106        return true;
107    }
108
109    // Skip ConstructorDecl and DestructorDecl.
110    if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
111      return true;
112  }
113
114  // Skip templated functions.
115  switch (Decl->getTemplatedKind()) {
116  case FunctionDecl::TK_NonTemplate:
117  case FunctionDecl::TK_DependentNonTemplate:
118    break;
119  case FunctionDecl::TK_MemberSpecialization:
120  case FunctionDecl::TK_FunctionTemplateSpecialization:
121    if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
122      if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
123        return true;
124    }
125    break;
126  case FunctionDecl::TK_FunctionTemplate:
127  case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
128    return true;
129  }
130
131  if (!LocationChecker(Decl->getLocation()))
132    return true;
133
134  // Collect symbol information.
135  StringRef Name = Decl->getName();
136  StringRef USR = API.recordUSR(Decl);
137  PresumedLoc Loc =
138      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
139  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
140  DocComment Comment;
141  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
142    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
143                                            Context.getDiagnostics());
144
145  // Build declaration fragments, sub-heading, and signature of the function.
146  DeclarationFragments Declaration =
147      DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
148  DeclarationFragments SubHeading =
149      DeclarationFragmentsBuilder::getSubHeading(Decl);
150  FunctionSignature Signature =
151      DeclarationFragmentsBuilder::getFunctionSignature(Decl);
152
153  // Add the function record to the API set.
154  API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
155                        Declaration, SubHeading, Signature,
156                        isInSystemHeader(Context, Decl));
157  return true;
158}
159
160bool ExtractAPIVisitor::VisitEnumDecl(const EnumDecl *Decl) {
161  if (!Decl->isComplete())
162    return true;
163
164  // Skip forward declaration.
165  if (!Decl->isThisDeclarationADefinition())
166    return true;
167
168  if (!LocationChecker(Decl->getLocation()))
169    return true;
170
171  SmallString<128> QualifiedNameBuffer;
172  // Collect symbol information.
173  StringRef Name = Decl->getName();
174  if (Name.empty())
175    Name = getTypedefName(Decl);
176  if (Name.empty()) {
177    llvm::raw_svector_ostream OS(QualifiedNameBuffer);
178    Decl->printQualifiedName(OS);
179    Name = QualifiedNameBuffer.str();
180  }
181
182  StringRef USR = API.recordUSR(Decl);
183  PresumedLoc Loc =
184      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
185  DocComment Comment;
186  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
187    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
188                                            Context.getDiagnostics());
189
190  // Build declaration fragments and sub-heading for the enum.
191  DeclarationFragments Declaration =
192      DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
193  DeclarationFragments SubHeading =
194      DeclarationFragmentsBuilder::getSubHeading(Decl);
195
196  EnumRecord *EnumRecord = API.addEnum(
197      API.copyString(Name), USR, Loc, AvailabilitySet(Decl), Comment,
198      Declaration, SubHeading, isInSystemHeader(Context, Decl));
199
200  // Now collect information about the enumerators in this enum.
201  recordEnumConstants(EnumRecord, Decl->enumerators());
202
203  return true;
204}
205
206bool ExtractAPIVisitor::VisitRecordDecl(const RecordDecl *Decl) {
207  if (!Decl->isCompleteDefinition())
208    return true;
209
210  // Skip C++ structs/classes/unions
211  // TODO: support C++ records
212  if (isa<CXXRecordDecl>(Decl))
213    return true;
214
215  if (!LocationChecker(Decl->getLocation()))
216    return true;
217
218  // Collect symbol information.
219  StringRef Name = Decl->getName();
220  if (Name.empty())
221    Name = getTypedefName(Decl);
222  if (Name.empty())
223    return true;
224
225  StringRef USR = API.recordUSR(Decl);
226  PresumedLoc Loc =
227      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
228  DocComment Comment;
229  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
230    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
231                                            Context.getDiagnostics());
232
233  // Build declaration fragments and sub-heading for the struct.
234  DeclarationFragments Declaration =
235      DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
236  DeclarationFragments SubHeading =
237      DeclarationFragmentsBuilder::getSubHeading(Decl);
238
239  StructRecord *StructRecord =
240      API.addStruct(Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration,
241                    SubHeading, isInSystemHeader(Context, Decl));
242
243  // Now collect information about the fields in this struct.
244  recordStructFields(StructRecord, Decl->fields());
245
246  return true;
247}
248
249bool ExtractAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
250  // Skip forward declaration for classes (@class)
251  if (!Decl->isThisDeclarationADefinition())
252    return true;
253
254  if (!LocationChecker(Decl->getLocation()))
255    return true;
256
257  // Collect symbol information.
258  StringRef Name = Decl->getName();
259  StringRef USR = API.recordUSR(Decl);
260  PresumedLoc Loc =
261      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
262  LinkageInfo Linkage = Decl->getLinkageAndVisibility();
263  DocComment Comment;
264  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
265    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
266                                            Context.getDiagnostics());
267
268  // Build declaration fragments and sub-heading for the interface.
269  DeclarationFragments Declaration =
270      DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
271  DeclarationFragments SubHeading =
272      DeclarationFragmentsBuilder::getSubHeading(Decl);
273
274  // Collect super class information.
275  SymbolReference SuperClass;
276  if (const auto *SuperClassDecl = Decl->getSuperClass()) {
277    SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
278    SuperClass.USR = API.recordUSR(SuperClassDecl);
279  }
280
281  ObjCInterfaceRecord *ObjCInterfaceRecord = API.addObjCInterface(
282      Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, Declaration,
283      SubHeading, SuperClass, isInSystemHeader(Context, Decl));
284
285  // Record all methods (selectors). This doesn't include automatically
286  // synthesized property methods.
287  recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
288  recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
289  recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
290  recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
291
292  return true;
293}
294
295bool ExtractAPIVisitor::VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
296  // Skip forward declaration for protocols (@protocol).
297  if (!Decl->isThisDeclarationADefinition())
298    return true;
299
300  if (!LocationChecker(Decl->getLocation()))
301    return true;
302
303  // Collect symbol information.
304  StringRef Name = Decl->getName();
305  StringRef USR = API.recordUSR(Decl);
306  PresumedLoc Loc =
307      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
308  DocComment Comment;
309  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
310    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
311                                            Context.getDiagnostics());
312
313  // Build declaration fragments and sub-heading for the protocol.
314  DeclarationFragments Declaration =
315      DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
316  DeclarationFragments SubHeading =
317      DeclarationFragmentsBuilder::getSubHeading(Decl);
318
319  ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
320      Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
321      isInSystemHeader(Context, Decl));
322
323  recordObjCMethods(ObjCProtocolRecord, Decl->methods());
324  recordObjCProperties(ObjCProtocolRecord, Decl->properties());
325  recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
326
327  return true;
328}
329
330bool ExtractAPIVisitor::VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
331  // Skip ObjC Type Parameter for now.
332  if (isa<ObjCTypeParamDecl>(Decl))
333    return true;
334
335  if (!Decl->isDefinedOutsideFunctionOrMethod())
336    return true;
337
338  if (!LocationChecker(Decl->getLocation()))
339    return true;
340
341  PresumedLoc Loc =
342      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
343  StringRef Name = Decl->getName();
344  StringRef USR = API.recordUSR(Decl);
345  DocComment Comment;
346  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
347    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
348                                            Context.getDiagnostics());
349
350  QualType Type = Decl->getUnderlyingType();
351  SymbolReference SymRef =
352      TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
353                                                                       API);
354
355  API.addTypedef(Name, USR, Loc, AvailabilitySet(Decl), Comment,
356                 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
357                 DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef,
358                 isInSystemHeader(Context, Decl));
359
360  return true;
361}
362
363bool ExtractAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
364  // Collect symbol information.
365  StringRef Name = Decl->getName();
366  StringRef USR = API.recordUSR(Decl);
367  PresumedLoc Loc =
368      Context.getSourceManager().getPresumedLoc(Decl->getLocation());
369  DocComment Comment;
370  if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
371    Comment = RawComment->getFormattedLines(Context.getSourceManager(),
372                                            Context.getDiagnostics());
373  // Build declaration fragments and sub-heading for the category.
374  DeclarationFragments Declaration =
375      DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
376  DeclarationFragments SubHeading =
377      DeclarationFragmentsBuilder::getSubHeading(Decl);
378
379  const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
380  SymbolReference Interface(InterfaceDecl->getName(),
381                            API.recordUSR(InterfaceDecl));
382
383  ObjCCategoryRecord *ObjCCategoryRecord = API.addObjCCategory(
384      Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
385      Interface, isInSystemHeader(Context, Decl));
386
387  recordObjCMethods(ObjCCategoryRecord, Decl->methods());
388  recordObjCProperties(ObjCCategoryRecord, Decl->properties());
389  recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
390  recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
391
392  return true;
393}
394
395/// Collect API information for the enum constants and associate with the
396/// parent enum.
397void ExtractAPIVisitor::recordEnumConstants(
398    EnumRecord *EnumRecord, const EnumDecl::enumerator_range Constants) {
399  for (const auto *Constant : Constants) {
400    // Collect symbol information.
401    StringRef Name = Constant->getName();
402    StringRef USR = API.recordUSR(Constant);
403    PresumedLoc Loc =
404        Context.getSourceManager().getPresumedLoc(Constant->getLocation());
405    DocComment Comment;
406    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
407      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
408                                              Context.getDiagnostics());
409
410    // Build declaration fragments and sub-heading for the enum constant.
411    DeclarationFragments Declaration =
412        DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
413    DeclarationFragments SubHeading =
414        DeclarationFragmentsBuilder::getSubHeading(Constant);
415
416    API.addEnumConstant(EnumRecord, Name, USR, Loc, AvailabilitySet(Constant),
417                        Comment, Declaration, SubHeading,
418                        isInSystemHeader(Context, Constant));
419  }
420}
421
422/// Collect API information for the struct fields and associate with the
423/// parent struct.
424void ExtractAPIVisitor::recordStructFields(
425    StructRecord *StructRecord, const RecordDecl::field_range Fields) {
426  for (const auto *Field : Fields) {
427    // Collect symbol information.
428    StringRef Name = Field->getName();
429    StringRef USR = API.recordUSR(Field);
430    PresumedLoc Loc =
431        Context.getSourceManager().getPresumedLoc(Field->getLocation());
432    DocComment Comment;
433    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
434      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
435                                              Context.getDiagnostics());
436
437    // Build declaration fragments and sub-heading for the struct field.
438    DeclarationFragments Declaration =
439        DeclarationFragmentsBuilder::getFragmentsForField(Field);
440    DeclarationFragments SubHeading =
441        DeclarationFragmentsBuilder::getSubHeading(Field);
442
443    API.addStructField(StructRecord, Name, USR, Loc, AvailabilitySet(Field),
444                       Comment, Declaration, SubHeading,
445                       isInSystemHeader(Context, Field));
446  }
447}
448
449/// Collect API information for the Objective-C methods and associate with the
450/// parent container.
451void ExtractAPIVisitor::recordObjCMethods(
452    ObjCContainerRecord *Container,
453    const ObjCContainerDecl::method_range Methods) {
454  for (const auto *Method : Methods) {
455    // Don't record selectors for properties.
456    if (Method->isPropertyAccessor())
457      continue;
458
459    StringRef Name = API.copyString(Method->getSelector().getAsString());
460    StringRef USR = API.recordUSR(Method);
461    PresumedLoc Loc =
462        Context.getSourceManager().getPresumedLoc(Method->getLocation());
463    DocComment Comment;
464    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
465      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
466                                              Context.getDiagnostics());
467
468    // Build declaration fragments, sub-heading, and signature for the method.
469    DeclarationFragments Declaration =
470        DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
471    DeclarationFragments SubHeading =
472        DeclarationFragmentsBuilder::getSubHeading(Method);
473    FunctionSignature Signature =
474        DeclarationFragmentsBuilder::getFunctionSignature(Method);
475
476    API.addObjCMethod(Container, Name, USR, Loc, AvailabilitySet(Method),
477                      Comment, Declaration, SubHeading, Signature,
478                      Method->isInstanceMethod(),
479                      isInSystemHeader(Context, Method));
480  }
481}
482
483void ExtractAPIVisitor::recordObjCProperties(
484    ObjCContainerRecord *Container,
485    const ObjCContainerDecl::prop_range Properties) {
486  for (const auto *Property : Properties) {
487    StringRef Name = Property->getName();
488    StringRef USR = API.recordUSR(Property);
489    PresumedLoc Loc =
490        Context.getSourceManager().getPresumedLoc(Property->getLocation());
491    DocComment Comment;
492    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
493      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
494                                              Context.getDiagnostics());
495
496    // Build declaration fragments and sub-heading for the property.
497    DeclarationFragments Declaration =
498        DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
499    DeclarationFragments SubHeading =
500        DeclarationFragmentsBuilder::getSubHeading(Property);
501
502    StringRef GetterName =
503        API.copyString(Property->getGetterName().getAsString());
504    StringRef SetterName =
505        API.copyString(Property->getSetterName().getAsString());
506
507    // Get the attributes for property.
508    unsigned Attributes = ObjCPropertyRecord::NoAttr;
509    if (Property->getPropertyAttributes() &
510        ObjCPropertyAttribute::kind_readonly)
511      Attributes |= ObjCPropertyRecord::ReadOnly;
512
513    API.addObjCProperty(
514        Container, Name, USR, Loc, AvailabilitySet(Property), Comment,
515        Declaration, SubHeading,
516        static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), GetterName,
517        SetterName, Property->isOptional(),
518        !(Property->getPropertyAttributes() &
519          ObjCPropertyAttribute::kind_class),
520        isInSystemHeader(Context, Property));
521  }
522}
523
524void ExtractAPIVisitor::recordObjCInstanceVariables(
525    ObjCContainerRecord *Container,
526    const llvm::iterator_range<
527        DeclContext::specific_decl_iterator<ObjCIvarDecl>>
528        Ivars) {
529  for (const auto *Ivar : Ivars) {
530    StringRef Name = Ivar->getName();
531    StringRef USR = API.recordUSR(Ivar);
532    PresumedLoc Loc =
533        Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
534    DocComment Comment;
535    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
536      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
537                                              Context.getDiagnostics());
538
539    // Build declaration fragments and sub-heading for the instance variable.
540    DeclarationFragments Declaration =
541        DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
542    DeclarationFragments SubHeading =
543        DeclarationFragmentsBuilder::getSubHeading(Ivar);
544
545    ObjCInstanceVariableRecord::AccessControl Access =
546        Ivar->getCanonicalAccessControl();
547
548    API.addObjCInstanceVariable(
549        Container, Name, USR, Loc, AvailabilitySet(Ivar), Comment, Declaration,
550        SubHeading, Access, isInSystemHeader(Context, Ivar));
551  }
552}
553
554void ExtractAPIVisitor::recordObjCProtocols(
555    ObjCContainerRecord *Container,
556    ObjCInterfaceDecl::protocol_range Protocols) {
557  for (const auto *Protocol : Protocols)
558    Container->Protocols.emplace_back(Protocol->getName(),
559                                      API.recordUSR(Protocol));
560}
561