xref: /llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp (revision 5891420e6848e86a069dc6b7b73f175e22388f4a)
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/DiagnosticFrontend.h"
24 #include "clang/Basic/SourceLocation.h"
25 #include "clang/Basic/SourceManager.h"
26 #include "clang/Basic/TargetInfo.h"
27 #include "clang/ExtractAPI/API.h"
28 #include "clang/ExtractAPI/APIIgnoresList.h"
29 #include "clang/ExtractAPI/AvailabilityInfo.h"
30 #include "clang/ExtractAPI/DeclarationFragments.h"
31 #include "clang/ExtractAPI/FrontendActions.h"
32 #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
33 #include "clang/Frontend/ASTConsumers.h"
34 #include "clang/Frontend/CompilerInstance.h"
35 #include "clang/Frontend/FrontendOptions.h"
36 #include "clang/Lex/MacroInfo.h"
37 #include "clang/Lex/PPCallbacks.h"
38 #include "clang/Lex/Preprocessor.h"
39 #include "clang/Lex/PreprocessorOptions.h"
40 #include "llvm/ADT/DenseSet.h"
41 #include "llvm/ADT/STLExtras.h"
42 #include "llvm/ADT/SmallVector.h"
43 #include "llvm/Support/Error.h"
44 #include "llvm/Support/FileSystem.h"
45 #include "llvm/Support/MemoryBuffer.h"
46 #include "llvm/Support/Path.h"
47 #include "llvm/Support/Regex.h"
48 #include "llvm/Support/raw_ostream.h"
49 #include <memory>
50 #include <utility>
51 
52 using namespace clang;
53 using namespace extractapi;
54 
55 namespace {
56 
57 StringRef getTypedefName(const TagDecl *Decl) {
58   if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
59     return TypedefDecl->getName();
60 
61   return {};
62 }
63 
64 Optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
65                                              StringRef File,
66                                              bool *IsQuoted = nullptr) {
67   assert(CI.hasFileManager() &&
68          "CompilerInstance does not have a FileNamager!");
69 
70   using namespace llvm::sys;
71   // Matches framework include patterns
72   const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)");
73 
74   const auto &FS = CI.getVirtualFileSystem();
75 
76   SmallString<128> FilePath(File.begin(), File.end());
77   FS.makeAbsolute(FilePath);
78   path::remove_dots(FilePath, true);
79   FilePath = path::convert_to_slash(FilePath);
80   File = FilePath;
81 
82   // Checks whether `Dir` is a strict path prefix of `File`. If so returns
83   // the prefix length. Otherwise return 0.
84   auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
85     llvm::SmallString<32> DirPath(Dir.begin(), Dir.end());
86     FS.makeAbsolute(DirPath);
87     path::remove_dots(DirPath, true);
88     Dir = DirPath;
89     for (auto NI = path::begin(File), NE = path::end(File),
90               DI = path::begin(Dir), DE = path::end(Dir);
91          /*termination condition in loop*/; ++NI, ++DI) {
92       // '.' components in File are ignored.
93       while (NI != NE && *NI == ".")
94         ++NI;
95       if (NI == NE)
96         break;
97 
98       // '.' components in Dir are ignored.
99       while (DI != DE && *DI == ".")
100         ++DI;
101 
102       // Dir is a prefix of File, up to '.' components and choice of path
103       // separators.
104       if (DI == DE)
105         return NI - path::begin(File);
106 
107       // Consider all path separators equal.
108       if (NI->size() == 1 && DI->size() == 1 &&
109           path::is_separator(NI->front()) && path::is_separator(DI->front()))
110         continue;
111 
112       // Special case Apple .sdk folders since the search path is typically a
113       // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
114       // located in `iPhoneSimulator.sdk` (the real folder).
115       if (NI->endswith(".sdk") && DI->endswith(".sdk")) {
116         StringRef NBasename = path::stem(*NI);
117         StringRef DBasename = path::stem(*DI);
118         if (DBasename.startswith(NBasename))
119           continue;
120       }
121 
122       if (*NI != *DI)
123         break;
124     }
125     return 0;
126   };
127 
128   unsigned PrefixLength = 0;
129 
130   // Go through the search paths and find the first one that is a prefix of
131   // the header.
132   for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) {
133     // Note whether the match is found in a quoted entry.
134     if (IsQuoted)
135       *IsQuoted = Entry.Group == frontend::Quoted;
136 
137     if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) {
138       if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) {
139         // If this is a headermap entry, try to reverse lookup the full path
140         // for a spelled name before mapping.
141         StringRef SpelledFilename = HMap->reverseLookupFilename(File);
142         if (!SpelledFilename.empty())
143           return SpelledFilename.str();
144 
145         // No matching mapping in this headermap, try next search entry.
146         continue;
147       }
148     }
149 
150     // Entry is a directory search entry, try to check if it's a prefix of File.
151     PrefixLength = CheckDir(Entry.Path);
152     if (PrefixLength > 0) {
153       // The header is found in a framework path, construct the framework-style
154       // include name `<Framework/Header.h>`
155       if (Entry.IsFramework) {
156         SmallVector<StringRef, 4> Matches;
157         Rule.match(File, &Matches);
158         // Returned matches are always in stable order.
159         if (Matches.size() != 4)
160           return std::nullopt;
161 
162         return path::convert_to_slash(
163             (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
164              Matches[3])
165                 .str());
166       }
167 
168       // The header is found in a normal search path, strip the search path
169       // prefix to get an include name.
170       return path::convert_to_slash(File.drop_front(PrefixLength));
171     }
172   }
173 
174   // Couldn't determine a include name, use full path instead.
175   return std::nullopt;
176 }
177 
178 struct LocationFileChecker {
179   bool isLocationInKnownFile(SourceLocation Loc) {
180     // If the loc refers to a macro expansion we need to first get the file
181     // location of the expansion.
182     auto &SM = CI.getSourceManager();
183     auto FileLoc = SM.getFileLoc(Loc);
184     FileID FID = SM.getFileID(FileLoc);
185     if (FID.isInvalid())
186       return false;
187 
188     const auto *File = SM.getFileEntryForID(FID);
189     if (!File)
190       return false;
191 
192     if (KnownFileEntries.count(File))
193       return true;
194 
195     if (ExternalFileEntries.count(File))
196       return false;
197 
198     StringRef FileName = File->tryGetRealPathName().empty()
199                              ? File->getName()
200                              : File->tryGetRealPathName();
201 
202     // Try to reduce the include name the same way we tried to include it.
203     bool IsQuoted = false;
204     if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted))
205       if (llvm::any_of(KnownFiles,
206                        [&IsQuoted, &IncludeName](const auto &KnownFile) {
207                          return KnownFile.first.equals(*IncludeName) &&
208                                 KnownFile.second == IsQuoted;
209                        })) {
210         KnownFileEntries.insert(File);
211         return true;
212       }
213 
214     // Record that the file was not found to avoid future reverse lookup for
215     // the same file.
216     ExternalFileEntries.insert(File);
217     return false;
218   }
219 
220   LocationFileChecker(const CompilerInstance &CI,
221                       SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
222       : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
223     for (const auto &KnownFile : KnownFiles)
224       if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first))
225         KnownFileEntries.insert(*FileEntry);
226   }
227 
228 private:
229   const CompilerInstance &CI;
230   SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
231   llvm::DenseSet<const FileEntry *> KnownFileEntries;
232   llvm::DenseSet<const FileEntry *> ExternalFileEntries;
233 };
234 
235 /// The RecursiveASTVisitor to traverse symbol declarations and collect API
236 /// information.
237 class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
238 public:
239   ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API)
240       : Context(Context), API(API), LCF(LCF) {}
241 
242   const APISet &getAPI() const { return API; }
243 
244   bool VisitVarDecl(const VarDecl *Decl) {
245     // Skip function parameters.
246     if (isa<ParmVarDecl>(Decl))
247       return true;
248 
249     // Skip non-global variables in records (struct/union/class).
250     if (Decl->getDeclContext()->isRecord())
251       return true;
252 
253     // Skip local variables inside function or method.
254     if (!Decl->isDefinedOutsideFunctionOrMethod())
255       return true;
256 
257     // If this is a template but not specialization or instantiation, skip.
258     if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
259         Decl->getTemplateSpecializationKind() == TSK_Undeclared)
260       return true;
261 
262     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
263       return true;
264 
265     // Collect symbol information.
266     StringRef Name = Decl->getName();
267     StringRef USR = API.recordUSR(Decl);
268     PresumedLoc Loc =
269         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
270     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
271     DocComment Comment;
272     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
273       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
274                                               Context.getDiagnostics());
275 
276     // Build declaration fragments and sub-heading for the variable.
277     DeclarationFragments Declaration =
278         DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
279     DeclarationFragments SubHeading =
280         DeclarationFragmentsBuilder::getSubHeading(Decl);
281 
282     // Add the global variable record to the API set.
283     API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
284                      Declaration, SubHeading);
285     return true;
286   }
287 
288   bool VisitFunctionDecl(const FunctionDecl *Decl) {
289     if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
290       // Skip member function in class templates.
291       if (Method->getParent()->getDescribedClassTemplate() != nullptr)
292         return true;
293 
294       // Skip methods in records.
295       for (auto P : Context.getParents(*Method)) {
296         if (P.get<CXXRecordDecl>())
297           return true;
298       }
299 
300       // Skip ConstructorDecl and DestructorDecl.
301       if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
302         return true;
303     }
304 
305     // Skip templated functions.
306     switch (Decl->getTemplatedKind()) {
307     case FunctionDecl::TK_NonTemplate:
308     case FunctionDecl::TK_DependentNonTemplate:
309       break;
310     case FunctionDecl::TK_MemberSpecialization:
311     case FunctionDecl::TK_FunctionTemplateSpecialization:
312       if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
313         if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
314           return true;
315       }
316       break;
317     case FunctionDecl::TK_FunctionTemplate:
318     case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
319       return true;
320     }
321 
322     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
323       return true;
324 
325     // Collect symbol information.
326     StringRef Name = Decl->getName();
327     StringRef USR = API.recordUSR(Decl);
328     PresumedLoc Loc =
329         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
330     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
331     DocComment Comment;
332     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
333       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
334                                               Context.getDiagnostics());
335 
336     // Build declaration fragments, sub-heading, and signature of the function.
337     DeclarationFragments Declaration =
338         DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
339     DeclarationFragments SubHeading =
340         DeclarationFragmentsBuilder::getSubHeading(Decl);
341     FunctionSignature Signature =
342         DeclarationFragmentsBuilder::getFunctionSignature(Decl);
343 
344     // Add the function record to the API set.
345     API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage,
346                           Comment, Declaration, SubHeading, Signature);
347     return true;
348   }
349 
350   bool VisitEnumDecl(const EnumDecl *Decl) {
351     if (!Decl->isComplete())
352       return true;
353 
354     // Skip forward declaration.
355     if (!Decl->isThisDeclarationADefinition())
356       return true;
357 
358     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
359       return true;
360 
361     // Collect symbol information.
362     std::string NameString = Decl->getQualifiedNameAsString();
363     StringRef Name(NameString);
364     if (Name.empty())
365       Name = getTypedefName(Decl);
366 
367     StringRef USR = API.recordUSR(Decl);
368     PresumedLoc Loc =
369         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
370     DocComment Comment;
371     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
372       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
373                                               Context.getDiagnostics());
374 
375     // Build declaration fragments and sub-heading for the enum.
376     DeclarationFragments Declaration =
377         DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
378     DeclarationFragments SubHeading =
379         DeclarationFragmentsBuilder::getSubHeading(Decl);
380 
381     EnumRecord *EnumRecord =
382         API.addEnum(API.copyString(Name), USR, Loc, AvailabilitySet(Decl),
383                     Comment, Declaration, SubHeading);
384 
385     // Now collect information about the enumerators in this enum.
386     recordEnumConstants(EnumRecord, Decl->enumerators());
387 
388     return true;
389   }
390 
391   bool VisitRecordDecl(const RecordDecl *Decl) {
392     if (!Decl->isCompleteDefinition())
393       return true;
394 
395     // Skip C++ structs/classes/unions
396     // TODO: support C++ records
397     if (isa<CXXRecordDecl>(Decl))
398       return true;
399 
400     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
401       return true;
402 
403     // Collect symbol information.
404     StringRef Name = Decl->getName();
405     if (Name.empty())
406       Name = getTypedefName(Decl);
407     if (Name.empty())
408       return true;
409 
410     StringRef USR = API.recordUSR(Decl);
411     PresumedLoc Loc =
412         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
413     DocComment Comment;
414     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
415       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
416                                               Context.getDiagnostics());
417 
418     // Build declaration fragments and sub-heading for the struct.
419     DeclarationFragments Declaration =
420         DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
421     DeclarationFragments SubHeading =
422         DeclarationFragmentsBuilder::getSubHeading(Decl);
423 
424     StructRecord *StructRecord =
425         API.addStruct(Name, USR, Loc, AvailabilitySet(Decl), Comment,
426                       Declaration, SubHeading);
427 
428     // Now collect information about the fields in this struct.
429     recordStructFields(StructRecord, Decl->fields());
430 
431     return true;
432   }
433 
434   bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
435     // Skip forward declaration for classes (@class)
436     if (!Decl->isThisDeclarationADefinition())
437       return true;
438 
439     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
440       return true;
441 
442     // Collect symbol information.
443     StringRef Name = Decl->getName();
444     StringRef USR = API.recordUSR(Decl);
445     PresumedLoc Loc =
446         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
447     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
448     DocComment Comment;
449     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
450       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
451                                               Context.getDiagnostics());
452 
453     // Build declaration fragments and sub-heading for the interface.
454     DeclarationFragments Declaration =
455         DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
456     DeclarationFragments SubHeading =
457         DeclarationFragmentsBuilder::getSubHeading(Decl);
458 
459     // Collect super class information.
460     SymbolReference SuperClass;
461     if (const auto *SuperClassDecl = Decl->getSuperClass()) {
462       SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
463       SuperClass.USR = API.recordUSR(SuperClassDecl);
464     }
465 
466     ObjCInterfaceRecord *ObjCInterfaceRecord =
467         API.addObjCInterface(Name, USR, Loc, AvailabilitySet(Decl), Linkage,
468                              Comment, Declaration, SubHeading, SuperClass);
469 
470     // Record all methods (selectors). This doesn't include automatically
471     // synthesized property methods.
472     recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
473     recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
474     recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
475     recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
476 
477     return true;
478   }
479 
480   bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
481     // Skip forward declaration for protocols (@protocol).
482     if (!Decl->isThisDeclarationADefinition())
483       return true;
484 
485     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
486       return true;
487 
488     // Collect symbol information.
489     StringRef Name = Decl->getName();
490     StringRef USR = API.recordUSR(Decl);
491     PresumedLoc Loc =
492         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
493     DocComment Comment;
494     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
495       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
496                                               Context.getDiagnostics());
497 
498     // Build declaration fragments and sub-heading for the protocol.
499     DeclarationFragments Declaration =
500         DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
501     DeclarationFragments SubHeading =
502         DeclarationFragmentsBuilder::getSubHeading(Decl);
503 
504     ObjCProtocolRecord *ObjCProtocolRecord =
505         API.addObjCProtocol(Name, USR, Loc, AvailabilitySet(Decl), Comment,
506                             Declaration, SubHeading);
507 
508     recordObjCMethods(ObjCProtocolRecord, Decl->methods());
509     recordObjCProperties(ObjCProtocolRecord, Decl->properties());
510     recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
511 
512     return true;
513   }
514 
515   bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
516     // Skip ObjC Type Parameter for now.
517     if (isa<ObjCTypeParamDecl>(Decl))
518       return true;
519 
520     if (!Decl->isDefinedOutsideFunctionOrMethod())
521       return true;
522 
523     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
524       return true;
525 
526     PresumedLoc Loc =
527         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
528     StringRef Name = Decl->getName();
529     StringRef USR = API.recordUSR(Decl);
530     DocComment Comment;
531     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
532       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
533                                               Context.getDiagnostics());
534 
535     QualType Type = Decl->getUnderlyingType();
536     SymbolReference SymRef =
537         TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
538                                                                          API);
539 
540     API.addTypedef(Name, USR, Loc, AvailabilitySet(Decl), Comment,
541                    DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
542                    DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
543 
544     return true;
545   }
546 
547   bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
548     // Collect symbol information.
549     StringRef Name = Decl->getName();
550     StringRef USR = API.recordUSR(Decl);
551     PresumedLoc Loc =
552         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
553     DocComment Comment;
554     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
555       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
556                                               Context.getDiagnostics());
557     // Build declaration fragments and sub-heading for the category.
558     DeclarationFragments Declaration =
559         DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
560     DeclarationFragments SubHeading =
561         DeclarationFragmentsBuilder::getSubHeading(Decl);
562 
563     const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
564     SymbolReference Interface(InterfaceDecl->getName(),
565                               API.recordUSR(InterfaceDecl));
566 
567     ObjCCategoryRecord *ObjCCategoryRecord =
568         API.addObjCCategory(Name, USR, Loc, AvailabilitySet(Decl), Comment,
569                             Declaration, SubHeading, Interface);
570 
571     recordObjCMethods(ObjCCategoryRecord, Decl->methods());
572     recordObjCProperties(ObjCCategoryRecord, Decl->properties());
573     recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
574     recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
575 
576     return true;
577   }
578 
579 private:
580   /// Collect API information for the enum constants and associate with the
581   /// parent enum.
582   void recordEnumConstants(EnumRecord *EnumRecord,
583                            const EnumDecl::enumerator_range Constants) {
584     for (const auto *Constant : Constants) {
585       // Collect symbol information.
586       StringRef Name = Constant->getName();
587       StringRef USR = API.recordUSR(Constant);
588       PresumedLoc Loc =
589           Context.getSourceManager().getPresumedLoc(Constant->getLocation());
590       DocComment Comment;
591       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
592         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
593                                                 Context.getDiagnostics());
594 
595       // Build declaration fragments and sub-heading for the enum constant.
596       DeclarationFragments Declaration =
597           DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
598       DeclarationFragments SubHeading =
599           DeclarationFragmentsBuilder::getSubHeading(Constant);
600 
601       API.addEnumConstant(EnumRecord, Name, USR, Loc, AvailabilitySet(Constant),
602                           Comment, Declaration, SubHeading);
603     }
604   }
605 
606   /// Collect API information for the struct fields and associate with the
607   /// parent struct.
608   void recordStructFields(StructRecord *StructRecord,
609                           const RecordDecl::field_range Fields) {
610     for (const auto *Field : Fields) {
611       // Collect symbol information.
612       StringRef Name = Field->getName();
613       StringRef USR = API.recordUSR(Field);
614       PresumedLoc Loc =
615           Context.getSourceManager().getPresumedLoc(Field->getLocation());
616       DocComment Comment;
617       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
618         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
619                                                 Context.getDiagnostics());
620 
621       // Build declaration fragments and sub-heading for the struct field.
622       DeclarationFragments Declaration =
623           DeclarationFragmentsBuilder::getFragmentsForField(Field);
624       DeclarationFragments SubHeading =
625           DeclarationFragmentsBuilder::getSubHeading(Field);
626 
627       API.addStructField(StructRecord, Name, USR, Loc, AvailabilitySet(Field),
628                          Comment, Declaration, SubHeading);
629     }
630   }
631 
632   /// Collect API information for the Objective-C methods and associate with the
633   /// parent container.
634   void recordObjCMethods(ObjCContainerRecord *Container,
635                          const ObjCContainerDecl::method_range Methods) {
636     for (const auto *Method : Methods) {
637       // Don't record selectors for properties.
638       if (Method->isPropertyAccessor())
639         continue;
640 
641       StringRef Name = API.copyString(Method->getSelector().getAsString());
642       StringRef USR = API.recordUSR(Method);
643       PresumedLoc Loc =
644           Context.getSourceManager().getPresumedLoc(Method->getLocation());
645       DocComment Comment;
646       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
647         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
648                                                 Context.getDiagnostics());
649 
650       // Build declaration fragments, sub-heading, and signature for the method.
651       DeclarationFragments Declaration =
652           DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
653       DeclarationFragments SubHeading =
654           DeclarationFragmentsBuilder::getSubHeading(Method);
655       FunctionSignature Signature =
656           DeclarationFragmentsBuilder::getFunctionSignature(Method);
657 
658       API.addObjCMethod(Container, Name, USR, Loc, AvailabilitySet(Method),
659                         Comment, Declaration, SubHeading, Signature,
660                         Method->isInstanceMethod());
661     }
662   }
663 
664   void recordObjCProperties(ObjCContainerRecord *Container,
665                             const ObjCContainerDecl::prop_range Properties) {
666     for (const auto *Property : Properties) {
667       StringRef Name = Property->getName();
668       StringRef USR = API.recordUSR(Property);
669       PresumedLoc Loc =
670           Context.getSourceManager().getPresumedLoc(Property->getLocation());
671       DocComment Comment;
672       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
673         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
674                                                 Context.getDiagnostics());
675 
676       // Build declaration fragments and sub-heading for the property.
677       DeclarationFragments Declaration =
678           DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
679       DeclarationFragments SubHeading =
680           DeclarationFragmentsBuilder::getSubHeading(Property);
681 
682       StringRef GetterName =
683           API.copyString(Property->getGetterName().getAsString());
684       StringRef SetterName =
685           API.copyString(Property->getSetterName().getAsString());
686 
687       // Get the attributes for property.
688       unsigned Attributes = ObjCPropertyRecord::NoAttr;
689       if (Property->getPropertyAttributes() &
690           ObjCPropertyAttribute::kind_readonly)
691         Attributes |= ObjCPropertyRecord::ReadOnly;
692       if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
693         Attributes |= ObjCPropertyRecord::Class;
694 
695       API.addObjCProperty(
696           Container, Name, USR, Loc, AvailabilitySet(Property), Comment,
697           Declaration, SubHeading,
698           static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
699           GetterName, SetterName, Property->isOptional());
700     }
701   }
702 
703   void recordObjCInstanceVariables(
704       ObjCContainerRecord *Container,
705       const llvm::iterator_range<
706           DeclContext::specific_decl_iterator<ObjCIvarDecl>>
707           Ivars) {
708     for (const auto *Ivar : Ivars) {
709       StringRef Name = Ivar->getName();
710       StringRef USR = API.recordUSR(Ivar);
711       PresumedLoc Loc =
712           Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
713       DocComment Comment;
714       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
715         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
716                                                 Context.getDiagnostics());
717 
718       // Build declaration fragments and sub-heading for the instance variable.
719       DeclarationFragments Declaration =
720           DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
721       DeclarationFragments SubHeading =
722           DeclarationFragmentsBuilder::getSubHeading(Ivar);
723 
724       ObjCInstanceVariableRecord::AccessControl Access =
725           Ivar->getCanonicalAccessControl();
726 
727       API.addObjCInstanceVariable(Container, Name, USR, Loc,
728                                   AvailabilitySet(Ivar), Comment, Declaration,
729                                   SubHeading, Access);
730     }
731   }
732 
733   void recordObjCProtocols(ObjCContainerRecord *Container,
734                            ObjCInterfaceDecl::protocol_range Protocols) {
735     for (const auto *Protocol : Protocols)
736       Container->Protocols.emplace_back(Protocol->getName(),
737                                         API.recordUSR(Protocol));
738   }
739 
740   ASTContext &Context;
741   APISet &API;
742   LocationFileChecker &LCF;
743 };
744 
745 class ExtractAPIConsumer : public ASTConsumer {
746 public:
747   ExtractAPIConsumer(ASTContext &Context,
748                      std::unique_ptr<LocationFileChecker> LCF, APISet &API)
749       : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {}
750 
751   void HandleTranslationUnit(ASTContext &Context) override {
752     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
753     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
754   }
755 
756 private:
757   ExtractAPIVisitor Visitor;
758   std::unique_ptr<LocationFileChecker> LCF;
759 };
760 
761 class MacroCallback : public PPCallbacks {
762 public:
763   MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API,
764                 Preprocessor &PP)
765       : SM(SM), LCF(LCF), API(API), PP(PP) {}
766 
767   void MacroDefined(const Token &MacroNameToken,
768                     const MacroDirective *MD) override {
769     auto *MacroInfo = MD->getMacroInfo();
770 
771     if (MacroInfo->isBuiltinMacro())
772       return;
773 
774     auto SourceLoc = MacroNameToken.getLocation();
775     if (SM.isWrittenInBuiltinFile(SourceLoc) ||
776         SM.isWrittenInCommandLineFile(SourceLoc))
777       return;
778 
779     PendingMacros.emplace_back(MacroNameToken, MD);
780   }
781 
782   // If a macro gets undefined at some point during preprocessing of the inputs
783   // it means that it isn't an exposed API and we should therefore not add a
784   // macro definition for it.
785   void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
786                       const MacroDirective *Undef) override {
787     // If this macro wasn't previously defined we don't need to do anything
788     // here.
789     if (!Undef)
790       return;
791 
792     llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
793       return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
794                                               /*Syntactically*/ false);
795     });
796   }
797 
798   void EndOfMainFile() override {
799     for (auto &PM : PendingMacros) {
800       // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
801       // file so check for it here.
802       if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
803         continue;
804 
805       if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation()))
806         continue;
807 
808       StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
809       PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
810       StringRef USR =
811           API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
812 
813       API.addMacroDefinition(
814           Name, USR, Loc,
815           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
816           DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
817     }
818 
819     PendingMacros.clear();
820   }
821 
822 private:
823   struct PendingMacro {
824     Token MacroNameToken;
825     const MacroDirective *MD;
826 
827     PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
828         : MacroNameToken(MacroNameToken), MD(MD) {}
829   };
830 
831   const SourceManager &SM;
832   LocationFileChecker &LCF;
833   APISet &API;
834   Preprocessor &PP;
835   llvm::SmallVector<PendingMacro> PendingMacros;
836 };
837 
838 } // namespace
839 
840 std::unique_ptr<ASTConsumer>
841 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
842   OS = CreateOutputFile(CI, InFile);
843   if (!OS)
844     return nullptr;
845 
846   ProductName = CI.getFrontendOpts().ProductName;
847 
848   // Now that we have enough information about the language options and the
849   // target triple, let's create the APISet before anyone uses it.
850   API = std::make_unique<APISet>(
851       CI.getTarget().getTriple(),
852       CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
853 
854   auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
855 
856   CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
857       CI.getSourceManager(), *LCF, *API, CI.getPreprocessor()));
858 
859   // Do not include location in anonymous decls.
860   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
861   Policy.AnonymousTagLocations = false;
862   CI.getASTContext().setPrintingPolicy(Policy);
863 
864   if (!CI.getFrontendOpts().ExtractAPIIgnoresFile.empty()) {
865     llvm::handleAllErrors(
866         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFile,
867                                CI.getFileManager())
868             .moveInto(IgnoresList),
869         [&CI](const IgnoresFileNotFound &Err) {
870           CI.getDiagnostics().Report(
871               diag::err_extract_api_ignores_file_not_found)
872               << Err.Path;
873         });
874   }
875 
876   return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
877                                               std::move(LCF), *API);
878 }
879 
880 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
881   auto &Inputs = CI.getFrontendOpts().Inputs;
882   if (Inputs.empty())
883     return true;
884 
885   if (!CI.hasFileManager())
886     if (!CI.createFileManager())
887       return false;
888 
889   auto Kind = Inputs[0].getKind();
890 
891   // Convert the header file inputs into a single input buffer.
892   SmallString<256> HeaderContents;
893   bool IsQuoted = false;
894   for (const FrontendInputFile &FIF : Inputs) {
895     if (Kind.isObjectiveC())
896       HeaderContents += "#import";
897     else
898       HeaderContents += "#include";
899 
900     StringRef FilePath = FIF.getFile();
901     if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
902       if (IsQuoted)
903         HeaderContents += " \"";
904       else
905         HeaderContents += " <";
906 
907       HeaderContents += *RelativeName;
908 
909       if (IsQuoted)
910         HeaderContents += "\"\n";
911       else
912         HeaderContents += ">\n";
913       KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
914                                    IsQuoted);
915     } else {
916       HeaderContents += " \"";
917       HeaderContents += FilePath;
918       HeaderContents += "\"\n";
919       KnownInputFiles.emplace_back(FilePath, true);
920     }
921   }
922 
923   if (CI.getHeaderSearchOpts().Verbose)
924     CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
925                                 << HeaderContents << "\n";
926 
927   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
928                                                 getInputBufferName());
929 
930   // Set that buffer up as our "real" input in the CompilerInstance.
931   Inputs.clear();
932   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
933 
934   return true;
935 }
936 
937 void ExtractAPIAction::EndSourceFileAction() {
938   if (!OS)
939     return;
940 
941   // Setup a SymbolGraphSerializer to write out collected API information in
942   // the Symbol Graph format.
943   // FIXME: Make the kind of APISerializer configurable.
944   SymbolGraphSerializer SGSerializer(*API, ProductName, IgnoresList);
945   SGSerializer.serialize(*OS);
946   OS.reset();
947 }
948 
949 std::unique_ptr<raw_pwrite_stream>
950 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
951   std::unique_ptr<raw_pwrite_stream> OS =
952       CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
953                                  /*RemoveFileOnSignal=*/false);
954   if (!OS)
955     return nullptr;
956   return OS;
957 }
958