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