1 //===- ExtractAPI/ExtractAPIConsumer.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 ExtractAPIAction, and ASTVisitor/Consumer to
11 /// collect API information.
12 ///
13 //===----------------------------------------------------------------------===//
14 
15 #include "TypedefUnderlyingTypeResolver.h"
16 #include "clang/AST/ASTConsumer.h"
17 #include "clang/AST/ASTContext.h"
18 #include "clang/AST/Decl.h"
19 #include "clang/AST/DeclCXX.h"
20 #include "clang/AST/ParentMapContext.h"
21 #include "clang/AST/RawCommentList.h"
22 #include "clang/AST/RecursiveASTVisitor.h"
23 #include "clang/Basic/SourceLocation.h"
24 #include "clang/Basic/SourceManager.h"
25 #include "clang/Basic/TargetInfo.h"
26 #include "clang/ExtractAPI/API.h"
27 #include "clang/ExtractAPI/AvailabilityInfo.h"
28 #include "clang/ExtractAPI/DeclarationFragments.h"
29 #include "clang/ExtractAPI/FrontendActions.h"
30 #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
31 #include "clang/Frontend/ASTConsumers.h"
32 #include "clang/Frontend/CompilerInstance.h"
33 #include "clang/Frontend/FrontendOptions.h"
34 #include "clang/Lex/MacroInfo.h"
35 #include "clang/Lex/PPCallbacks.h"
36 #include "clang/Lex/Preprocessor.h"
37 #include "clang/Lex/PreprocessorOptions.h"
38 #include "llvm/ADT/DenseSet.h"
39 #include "llvm/ADT/STLExtras.h"
40 #include "llvm/ADT/SmallVector.h"
41 #include "llvm/Support/MemoryBuffer.h"
42 #include "llvm/Support/raw_ostream.h"
43 #include <memory>
44 #include <utility>
45 
46 using namespace clang;
47 using namespace extractapi;
48 
49 namespace {
50 
51 StringRef getTypedefName(const TagDecl *Decl) {
52   if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
53     return TypedefDecl->getName();
54 
55   return {};
56 }
57 
58 struct LocationFileChecker {
59   bool isLocationInKnownFile(SourceLocation Loc) {
60     // If the loc refers to a macro expansion we need to first get the file
61     // location of the expansion.
62     auto FileLoc = SM.getFileLoc(Loc);
63     FileID FID = SM.getFileID(FileLoc);
64     if (FID.isInvalid())
65       return false;
66 
67     const auto *File = SM.getFileEntryForID(FID);
68     if (!File)
69       return false;
70 
71     if (KnownFileEntries.count(File))
72       return true;
73 
74     return false;
75   }
76 
77   LocationFileChecker(const SourceManager &SM,
78                       const std::vector<std::string> &KnownFiles)
79       : SM(SM) {
80     for (const auto &KnownFilePath : KnownFiles)
81       if (auto FileEntry = SM.getFileManager().getFile(KnownFilePath))
82         KnownFileEntries.insert(*FileEntry);
83   }
84 
85 private:
86   const SourceManager &SM;
87   llvm::DenseSet<const FileEntry *> KnownFileEntries;
88 };
89 
90 /// The RecursiveASTVisitor to traverse symbol declarations and collect API
91 /// information.
92 class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
93 public:
94   ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API)
95       : Context(Context), API(API), LCF(LCF) {}
96 
97   const APISet &getAPI() const { return API; }
98 
99   bool VisitVarDecl(const VarDecl *Decl) {
100     // Skip function parameters.
101     if (isa<ParmVarDecl>(Decl))
102       return true;
103 
104     // Skip non-global variables in records (struct/union/class).
105     if (Decl->getDeclContext()->isRecord())
106       return true;
107 
108     // Skip local variables inside function or method.
109     if (!Decl->isDefinedOutsideFunctionOrMethod())
110       return true;
111 
112     // If this is a template but not specialization or instantiation, skip.
113     if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
114         Decl->getTemplateSpecializationKind() == TSK_Undeclared)
115       return true;
116 
117     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
118       return true;
119 
120     // Collect symbol information.
121     StringRef Name = Decl->getName();
122     StringRef USR = API.recordUSR(Decl);
123     PresumedLoc Loc =
124         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
125     AvailabilityInfo Availability = getAvailability(Decl);
126     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
127     DocComment Comment;
128     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
129       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
130                                               Context.getDiagnostics());
131 
132     // Build declaration fragments and sub-heading for the variable.
133     DeclarationFragments Declaration =
134         DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
135     DeclarationFragments SubHeading =
136         DeclarationFragmentsBuilder::getSubHeading(Decl);
137 
138     // Add the global variable record to the API set.
139     API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment,
140                      Declaration, SubHeading);
141     return true;
142   }
143 
144   bool VisitFunctionDecl(const FunctionDecl *Decl) {
145     if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
146       // Skip member function in class templates.
147       if (Method->getParent()->getDescribedClassTemplate() != nullptr)
148         return true;
149 
150       // Skip methods in records.
151       for (auto P : Context.getParents(*Method)) {
152         if (P.get<CXXRecordDecl>())
153           return true;
154       }
155 
156       // Skip ConstructorDecl and DestructorDecl.
157       if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
158         return true;
159     }
160 
161     // Skip templated functions.
162     switch (Decl->getTemplatedKind()) {
163     case FunctionDecl::TK_NonTemplate:
164     case FunctionDecl::TK_DependentNonTemplate:
165       break;
166     case FunctionDecl::TK_MemberSpecialization:
167     case FunctionDecl::TK_FunctionTemplateSpecialization:
168       if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
169         if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
170           return true;
171       }
172       break;
173     case FunctionDecl::TK_FunctionTemplate:
174     case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
175       return true;
176     }
177 
178     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
179       return true;
180 
181     // Collect symbol information.
182     StringRef Name = Decl->getName();
183     StringRef USR = API.recordUSR(Decl);
184     PresumedLoc Loc =
185         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
186     AvailabilityInfo Availability = getAvailability(Decl);
187     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
188     DocComment Comment;
189     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
190       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
191                                               Context.getDiagnostics());
192 
193     // Build declaration fragments, sub-heading, and signature of the function.
194     DeclarationFragments Declaration =
195         DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
196     DeclarationFragments SubHeading =
197         DeclarationFragmentsBuilder::getSubHeading(Decl);
198     FunctionSignature Signature =
199         DeclarationFragmentsBuilder::getFunctionSignature(Decl);
200 
201     // Add the function record to the API set.
202     API.addGlobalFunction(Name, USR, Loc, Availability, Linkage, Comment,
203                           Declaration, SubHeading, Signature);
204     return true;
205   }
206 
207   bool VisitEnumDecl(const EnumDecl *Decl) {
208     if (!Decl->isComplete())
209       return true;
210 
211     // Skip forward declaration.
212     if (!Decl->isThisDeclarationADefinition())
213       return true;
214 
215     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
216       return true;
217 
218     // Collect symbol information.
219     std::string NameString = Decl->getQualifiedNameAsString();
220     StringRef Name(NameString);
221     if (Name.empty())
222       Name = getTypedefName(Decl);
223 
224     StringRef USR = API.recordUSR(Decl);
225     PresumedLoc Loc =
226         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
227     AvailabilityInfo Availability = getAvailability(Decl);
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 enum.
234     DeclarationFragments Declaration =
235         DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
236     DeclarationFragments SubHeading =
237         DeclarationFragmentsBuilder::getSubHeading(Decl);
238 
239     EnumRecord *EnumRecord =
240         API.addEnum(API.copyString(Name), USR, Loc, Availability, Comment,
241                     Declaration, SubHeading);
242 
243     // Now collect information about the enumerators in this enum.
244     recordEnumConstants(EnumRecord, Decl->enumerators());
245 
246     return true;
247   }
248 
249   bool VisitRecordDecl(const RecordDecl *Decl) {
250     if (!Decl->isCompleteDefinition())
251       return true;
252 
253     // Skip C++ structs/classes/unions
254     // TODO: support C++ records
255     if (isa<CXXRecordDecl>(Decl))
256       return true;
257 
258     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
259       return true;
260 
261     // Collect symbol information.
262     StringRef Name = Decl->getName();
263     if (Name.empty())
264       Name = getTypedefName(Decl);
265     StringRef USR = API.recordUSR(Decl);
266     PresumedLoc Loc =
267         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
268     AvailabilityInfo Availability = getAvailability(Decl);
269     DocComment Comment;
270     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
271       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
272                                               Context.getDiagnostics());
273 
274     // Build declaration fragments and sub-heading for the struct.
275     DeclarationFragments Declaration =
276         DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
277     DeclarationFragments SubHeading =
278         DeclarationFragmentsBuilder::getSubHeading(Decl);
279 
280     StructRecord *StructRecord = API.addStruct(
281         Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
282 
283     // Now collect information about the fields in this struct.
284     recordStructFields(StructRecord, Decl->fields());
285 
286     return true;
287   }
288 
289   bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
290     // Skip forward declaration for classes (@class)
291     if (!Decl->isThisDeclarationADefinition())
292       return true;
293 
294     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
295       return true;
296 
297     // Collect symbol information.
298     StringRef Name = Decl->getName();
299     StringRef USR = API.recordUSR(Decl);
300     PresumedLoc Loc =
301         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
302     AvailabilityInfo Availability = getAvailability(Decl);
303     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
304     DocComment Comment;
305     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
306       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
307                                               Context.getDiagnostics());
308 
309     // Build declaration fragments and sub-heading for the interface.
310     DeclarationFragments Declaration =
311         DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
312     DeclarationFragments SubHeading =
313         DeclarationFragmentsBuilder::getSubHeading(Decl);
314 
315     // Collect super class information.
316     SymbolReference SuperClass;
317     if (const auto *SuperClassDecl = Decl->getSuperClass()) {
318       SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
319       SuperClass.USR = API.recordUSR(SuperClassDecl);
320     }
321 
322     ObjCInterfaceRecord *ObjCInterfaceRecord =
323         API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment,
324                              Declaration, SubHeading, SuperClass);
325 
326     // Record all methods (selectors). This doesn't include automatically
327     // synthesized property methods.
328     recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
329     recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
330     recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
331     recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
332 
333     return true;
334   }
335 
336   bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
337     // Skip forward declaration for protocols (@protocol).
338     if (!Decl->isThisDeclarationADefinition())
339       return true;
340 
341     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
342       return true;
343 
344     // Collect symbol information.
345     StringRef Name = Decl->getName();
346     StringRef USR = API.recordUSR(Decl);
347     PresumedLoc Loc =
348         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
349     AvailabilityInfo Availability = getAvailability(Decl);
350     DocComment Comment;
351     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
352       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
353                                               Context.getDiagnostics());
354 
355     // Build declaration fragments and sub-heading for the protocol.
356     DeclarationFragments Declaration =
357         DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
358     DeclarationFragments SubHeading =
359         DeclarationFragmentsBuilder::getSubHeading(Decl);
360 
361     ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
362         Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
363 
364     recordObjCMethods(ObjCProtocolRecord, Decl->methods());
365     recordObjCProperties(ObjCProtocolRecord, Decl->properties());
366     recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
367 
368     return true;
369   }
370 
371   bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
372     // Skip ObjC Type Parameter for now.
373     if (isa<ObjCTypeParamDecl>(Decl))
374       return true;
375 
376     if (!Decl->isDefinedOutsideFunctionOrMethod())
377       return true;
378 
379     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
380       return true;
381 
382     PresumedLoc Loc =
383         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
384     StringRef Name = Decl->getName();
385     AvailabilityInfo Availability = getAvailability(Decl);
386     StringRef USR = API.recordUSR(Decl);
387     DocComment Comment;
388     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
389       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
390                                               Context.getDiagnostics());
391 
392     QualType Type = Decl->getUnderlyingType();
393     SymbolReference SymRef =
394         TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
395                                                                          API);
396 
397     API.addTypedef(Name, USR, Loc, Availability, Comment,
398                    DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
399                    DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
400 
401     return true;
402   }
403 
404   bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
405     // Collect symbol information.
406     StringRef Name = Decl->getName();
407     StringRef USR = API.recordUSR(Decl);
408     PresumedLoc Loc =
409         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
410     AvailabilityInfo Availability = getAvailability(Decl);
411     DocComment Comment;
412     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
413       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
414                                               Context.getDiagnostics());
415     // Build declaration fragments and sub-heading for the category.
416     DeclarationFragments Declaration =
417         DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
418     DeclarationFragments SubHeading =
419         DeclarationFragmentsBuilder::getSubHeading(Decl);
420 
421     const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
422     SymbolReference Interface(InterfaceDecl->getName(),
423                               API.recordUSR(InterfaceDecl));
424 
425     ObjCCategoryRecord *ObjCCategoryRecord =
426         API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration,
427                             SubHeading, Interface);
428 
429     recordObjCMethods(ObjCCategoryRecord, Decl->methods());
430     recordObjCProperties(ObjCCategoryRecord, Decl->properties());
431     recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
432     recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
433 
434     return true;
435   }
436 
437 private:
438   /// Get availability information of the declaration \p D.
439   AvailabilityInfo getAvailability(const Decl *D) const {
440     StringRef PlatformName = Context.getTargetInfo().getPlatformName();
441 
442     AvailabilityInfo Availability;
443     // Collect availability attributes from all redeclarations.
444     for (const auto *RD : D->redecls()) {
445       for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
446         if (A->getPlatform()->getName() != PlatformName)
447           continue;
448         Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
449                                         A->getObsoleted(), A->getUnavailable(),
450                                         /* UnconditionallyDeprecated */ false,
451                                         /* UnconditionallyUnavailable */ false);
452         break;
453       }
454 
455       if (const auto *A = RD->getAttr<UnavailableAttr>())
456         if (!A->isImplicit()) {
457           Availability.Unavailable = true;
458           Availability.UnconditionallyUnavailable = true;
459         }
460 
461       if (const auto *A = RD->getAttr<DeprecatedAttr>())
462         if (!A->isImplicit())
463           Availability.UnconditionallyDeprecated = true;
464     }
465 
466     return Availability;
467   }
468 
469   /// Collect API information for the enum constants and associate with the
470   /// parent enum.
471   void recordEnumConstants(EnumRecord *EnumRecord,
472                            const EnumDecl::enumerator_range Constants) {
473     for (const auto *Constant : Constants) {
474       // Collect symbol information.
475       StringRef Name = Constant->getName();
476       StringRef USR = API.recordUSR(Constant);
477       PresumedLoc Loc =
478           Context.getSourceManager().getPresumedLoc(Constant->getLocation());
479       AvailabilityInfo Availability = getAvailability(Constant);
480       DocComment Comment;
481       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
482         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
483                                                 Context.getDiagnostics());
484 
485       // Build declaration fragments and sub-heading for the enum constant.
486       DeclarationFragments Declaration =
487           DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
488       DeclarationFragments SubHeading =
489           DeclarationFragmentsBuilder::getSubHeading(Constant);
490 
491       API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
492                           Declaration, SubHeading);
493     }
494   }
495 
496   /// Collect API information for the struct fields and associate with the
497   /// parent struct.
498   void recordStructFields(StructRecord *StructRecord,
499                           const RecordDecl::field_range Fields) {
500     for (const auto *Field : Fields) {
501       // Collect symbol information.
502       StringRef Name = Field->getName();
503       StringRef USR = API.recordUSR(Field);
504       PresumedLoc Loc =
505           Context.getSourceManager().getPresumedLoc(Field->getLocation());
506       AvailabilityInfo Availability = getAvailability(Field);
507       DocComment Comment;
508       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
509         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
510                                                 Context.getDiagnostics());
511 
512       // Build declaration fragments and sub-heading for the struct field.
513       DeclarationFragments Declaration =
514           DeclarationFragmentsBuilder::getFragmentsForField(Field);
515       DeclarationFragments SubHeading =
516           DeclarationFragmentsBuilder::getSubHeading(Field);
517 
518       API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
519                          Declaration, SubHeading);
520     }
521   }
522 
523   /// Collect API information for the Objective-C methods and associate with the
524   /// parent container.
525   void recordObjCMethods(ObjCContainerRecord *Container,
526                          const ObjCContainerDecl::method_range Methods) {
527     for (const auto *Method : Methods) {
528       // Don't record selectors for properties.
529       if (Method->isPropertyAccessor())
530         continue;
531 
532       StringRef Name = API.copyString(Method->getSelector().getAsString());
533       StringRef USR = API.recordUSR(Method);
534       PresumedLoc Loc =
535           Context.getSourceManager().getPresumedLoc(Method->getLocation());
536       AvailabilityInfo Availability = getAvailability(Method);
537       DocComment Comment;
538       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
539         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
540                                                 Context.getDiagnostics());
541 
542       // Build declaration fragments, sub-heading, and signature for the method.
543       DeclarationFragments Declaration =
544           DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
545       DeclarationFragments SubHeading =
546           DeclarationFragmentsBuilder::getSubHeading(Method);
547       FunctionSignature Signature =
548           DeclarationFragmentsBuilder::getFunctionSignature(Method);
549 
550       API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
551                         Declaration, SubHeading, Signature,
552                         Method->isInstanceMethod());
553     }
554   }
555 
556   void recordObjCProperties(ObjCContainerRecord *Container,
557                             const ObjCContainerDecl::prop_range Properties) {
558     for (const auto *Property : Properties) {
559       StringRef Name = Property->getName();
560       StringRef USR = API.recordUSR(Property);
561       PresumedLoc Loc =
562           Context.getSourceManager().getPresumedLoc(Property->getLocation());
563       AvailabilityInfo Availability = getAvailability(Property);
564       DocComment Comment;
565       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
566         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
567                                                 Context.getDiagnostics());
568 
569       // Build declaration fragments and sub-heading for the property.
570       DeclarationFragments Declaration =
571           DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
572       DeclarationFragments SubHeading =
573           DeclarationFragmentsBuilder::getSubHeading(Property);
574 
575       StringRef GetterName =
576           API.copyString(Property->getGetterName().getAsString());
577       StringRef SetterName =
578           API.copyString(Property->getSetterName().getAsString());
579 
580       // Get the attributes for property.
581       unsigned Attributes = ObjCPropertyRecord::NoAttr;
582       if (Property->getPropertyAttributes() &
583           ObjCPropertyAttribute::kind_readonly)
584         Attributes |= ObjCPropertyRecord::ReadOnly;
585       if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
586         Attributes |= ObjCPropertyRecord::Class;
587 
588       API.addObjCProperty(
589           Container, Name, USR, Loc, Availability, Comment, Declaration,
590           SubHeading,
591           static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
592           GetterName, SetterName, Property->isOptional());
593     }
594   }
595 
596   void recordObjCInstanceVariables(
597       ObjCContainerRecord *Container,
598       const llvm::iterator_range<
599           DeclContext::specific_decl_iterator<ObjCIvarDecl>>
600           Ivars) {
601     for (const auto *Ivar : Ivars) {
602       StringRef Name = Ivar->getName();
603       StringRef USR = API.recordUSR(Ivar);
604       PresumedLoc Loc =
605           Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
606       AvailabilityInfo Availability = getAvailability(Ivar);
607       DocComment Comment;
608       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
609         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
610                                                 Context.getDiagnostics());
611 
612       // Build declaration fragments and sub-heading for the instance variable.
613       DeclarationFragments Declaration =
614           DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
615       DeclarationFragments SubHeading =
616           DeclarationFragmentsBuilder::getSubHeading(Ivar);
617 
618       ObjCInstanceVariableRecord::AccessControl Access =
619           Ivar->getCanonicalAccessControl();
620 
621       API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
622                                   Comment, Declaration, SubHeading, Access);
623     }
624   }
625 
626   void recordObjCProtocols(ObjCContainerRecord *Container,
627                            ObjCInterfaceDecl::protocol_range Protocols) {
628     for (const auto *Protocol : Protocols)
629       Container->Protocols.emplace_back(Protocol->getName(),
630                                         API.recordUSR(Protocol));
631   }
632 
633   ASTContext &Context;
634   APISet &API;
635   LocationFileChecker &LCF;
636 };
637 
638 class ExtractAPIConsumer : public ASTConsumer {
639 public:
640   ExtractAPIConsumer(ASTContext &Context,
641                      std::unique_ptr<LocationFileChecker> LCF, APISet &API)
642       : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {}
643 
644   void HandleTranslationUnit(ASTContext &Context) override {
645     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
646     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
647   }
648 
649 private:
650   ExtractAPIVisitor Visitor;
651   std::unique_ptr<LocationFileChecker> LCF;
652 };
653 
654 class MacroCallback : public PPCallbacks {
655 public:
656   MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API,
657                 Preprocessor &PP)
658       : SM(SM), LCF(LCF), API(API), PP(PP) {}
659 
660   void MacroDefined(const Token &MacroNameToken,
661                     const MacroDirective *MD) override {
662     auto *MacroInfo = MD->getMacroInfo();
663 
664     if (MacroInfo->isBuiltinMacro())
665       return;
666 
667     auto SourceLoc = MacroNameToken.getLocation();
668     if (SM.isWrittenInBuiltinFile(SourceLoc) ||
669         SM.isWrittenInCommandLineFile(SourceLoc))
670       return;
671 
672     PendingMacros.emplace_back(MacroNameToken, MD);
673   }
674 
675   // If a macro gets undefined at some point during preprocessing of the inputs
676   // it means that it isn't an exposed API and we should therefore not add a
677   // macro definition for it.
678   void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
679                       const MacroDirective *Undef) override {
680     // If this macro wasn't previously defined we don't need to do anything
681     // here.
682     if (!Undef)
683       return;
684 
685     llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
686       return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
687                                               /*Syntactically*/ false);
688     });
689   }
690 
691   void EndOfMainFile() override {
692     for (auto &PM : PendingMacros) {
693       // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
694       // file so check for it here.
695       if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
696         continue;
697 
698       if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation()))
699         continue;
700 
701       StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
702       PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
703       StringRef USR =
704           API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
705 
706       API.addMacroDefinition(
707           Name, USR, Loc,
708           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
709           DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
710     }
711 
712     PendingMacros.clear();
713   }
714 
715 private:
716   struct PendingMacro {
717     Token MacroNameToken;
718     const MacroDirective *MD;
719 
720     PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
721         : MacroNameToken(MacroNameToken), MD(MD) {}
722   };
723 
724   const SourceManager &SM;
725   LocationFileChecker &LCF;
726   APISet &API;
727   Preprocessor &PP;
728   llvm::SmallVector<PendingMacro> PendingMacros;
729 };
730 
731 } // namespace
732 
733 std::unique_ptr<ASTConsumer>
734 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
735   OS = CreateOutputFile(CI, InFile);
736   if (!OS)
737     return nullptr;
738 
739   ProductName = CI.getFrontendOpts().ProductName;
740 
741   // Now that we have enough information about the language options and the
742   // target triple, let's create the APISet before anyone uses it.
743   API = std::make_unique<APISet>(
744       CI.getTarget().getTriple(),
745       CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
746 
747   auto LCF = std::make_unique<LocationFileChecker>(CI.getSourceManager(),
748                                                    KnownInputFiles);
749 
750   CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
751       CI.getSourceManager(), *LCF, *API, CI.getPreprocessor()));
752 
753   return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
754                                               std::move(LCF), *API);
755 }
756 
757 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
758   auto &Inputs = CI.getFrontendOpts().Inputs;
759   if (Inputs.empty())
760     return true;
761 
762   auto Kind = Inputs[0].getKind();
763 
764   // Convert the header file inputs into a single input buffer.
765   SmallString<256> HeaderContents;
766   for (const FrontendInputFile &FIF : Inputs) {
767     if (Kind.isObjectiveC())
768       HeaderContents += "#import";
769     else
770       HeaderContents += "#include";
771     HeaderContents += " \"";
772     HeaderContents += FIF.getFile();
773     HeaderContents += "\"\n";
774 
775     KnownInputFiles.emplace_back(FIF.getFile());
776   }
777 
778   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
779                                                 getInputBufferName());
780 
781   // Set that buffer up as our "real" input in the CompilerInstance.
782   Inputs.clear();
783   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
784 
785   return true;
786 }
787 
788 void ExtractAPIAction::EndSourceFileAction() {
789   if (!OS)
790     return;
791 
792   // Setup a SymbolGraphSerializer to write out collected API information in
793   // the Symbol Graph format.
794   // FIXME: Make the kind of APISerializer configurable.
795   SymbolGraphSerializer SGSerializer(*API, ProductName);
796   SGSerializer.serialize(*OS);
797   OS.reset();
798 }
799 
800 std::unique_ptr<raw_pwrite_stream>
801 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
802   std::unique_ptr<raw_pwrite_stream> OS =
803       CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
804                                  /*RemoveFileOnSignal=*/false);
805   if (!OS)
806     return nullptr;
807   return OS;
808 }
809