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