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