xref: /freebsd-src/contrib/llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
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       break;
307     case FunctionDecl::TK_MemberSpecialization:
308     case FunctionDecl::TK_FunctionTemplateSpecialization:
309       if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
310         if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
311           return true;
312       }
313       break;
314     case FunctionDecl::TK_FunctionTemplate:
315     case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
316       return true;
317     }
318 
319     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
320       return true;
321 
322     // Collect symbol information.
323     StringRef Name = Decl->getName();
324     StringRef USR = API.recordUSR(Decl);
325     PresumedLoc Loc =
326         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
327     AvailabilityInfo Availability = getAvailability(Decl);
328     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
329     DocComment Comment;
330     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
331       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
332                                               Context.getDiagnostics());
333 
334     // Build declaration fragments, sub-heading, and signature of the function.
335     DeclarationFragments Declaration =
336         DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
337     DeclarationFragments SubHeading =
338         DeclarationFragmentsBuilder::getSubHeading(Decl);
339     FunctionSignature Signature =
340         DeclarationFragmentsBuilder::getFunctionSignature(Decl);
341 
342     // Add the function record to the API set.
343     API.addGlobalFunction(Name, USR, Loc, Availability, Linkage, Comment,
344                           Declaration, SubHeading, Signature);
345     return true;
346   }
347 
348   bool VisitEnumDecl(const EnumDecl *Decl) {
349     if (!Decl->isComplete())
350       return true;
351 
352     // Skip forward declaration.
353     if (!Decl->isThisDeclarationADefinition())
354       return true;
355 
356     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
357       return true;
358 
359     // Collect symbol information.
360     std::string NameString = Decl->getQualifiedNameAsString();
361     StringRef Name(NameString);
362     if (Name.empty())
363       Name = getTypedefName(Decl);
364 
365     StringRef USR = API.recordUSR(Decl);
366     PresumedLoc Loc =
367         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
368     AvailabilityInfo Availability = getAvailability(Decl);
369     DocComment Comment;
370     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
371       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
372                                               Context.getDiagnostics());
373 
374     // Build declaration fragments and sub-heading for the enum.
375     DeclarationFragments Declaration =
376         DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
377     DeclarationFragments SubHeading =
378         DeclarationFragmentsBuilder::getSubHeading(Decl);
379 
380     EnumRecord *EnumRecord =
381         API.addEnum(API.copyString(Name), USR, Loc, Availability, Comment,
382                     Declaration, SubHeading);
383 
384     // Now collect information about the enumerators in this enum.
385     recordEnumConstants(EnumRecord, Decl->enumerators());
386 
387     return true;
388   }
389 
390   bool VisitRecordDecl(const RecordDecl *Decl) {
391     if (!Decl->isCompleteDefinition())
392       return true;
393 
394     // Skip C++ structs/classes/unions
395     // TODO: support C++ records
396     if (isa<CXXRecordDecl>(Decl))
397       return true;
398 
399     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
400       return true;
401 
402     // Collect symbol information.
403     StringRef Name = Decl->getName();
404     if (Name.empty())
405       Name = getTypedefName(Decl);
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 
415     // Build declaration fragments and sub-heading for the struct.
416     DeclarationFragments Declaration =
417         DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
418     DeclarationFragments SubHeading =
419         DeclarationFragmentsBuilder::getSubHeading(Decl);
420 
421     StructRecord *StructRecord = API.addStruct(
422         Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
423 
424     // Now collect information about the fields in this struct.
425     recordStructFields(StructRecord, Decl->fields());
426 
427     return true;
428   }
429 
430   bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
431     // Skip forward declaration for classes (@class)
432     if (!Decl->isThisDeclarationADefinition())
433       return true;
434 
435     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
436       return true;
437 
438     // Collect symbol information.
439     StringRef Name = Decl->getName();
440     StringRef USR = API.recordUSR(Decl);
441     PresumedLoc Loc =
442         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
443     AvailabilityInfo Availability = getAvailability(Decl);
444     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
445     DocComment Comment;
446     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
447       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
448                                               Context.getDiagnostics());
449 
450     // Build declaration fragments and sub-heading for the interface.
451     DeclarationFragments Declaration =
452         DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
453     DeclarationFragments SubHeading =
454         DeclarationFragmentsBuilder::getSubHeading(Decl);
455 
456     // Collect super class information.
457     SymbolReference SuperClass;
458     if (const auto *SuperClassDecl = Decl->getSuperClass()) {
459       SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
460       SuperClass.USR = API.recordUSR(SuperClassDecl);
461     }
462 
463     ObjCInterfaceRecord *ObjCInterfaceRecord =
464         API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment,
465                              Declaration, SubHeading, SuperClass);
466 
467     // Record all methods (selectors). This doesn't include automatically
468     // synthesized property methods.
469     recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
470     recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
471     recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
472     recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
473 
474     return true;
475   }
476 
477   bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
478     // Skip forward declaration for protocols (@protocol).
479     if (!Decl->isThisDeclarationADefinition())
480       return true;
481 
482     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
483       return true;
484 
485     // Collect symbol information.
486     StringRef Name = Decl->getName();
487     StringRef USR = API.recordUSR(Decl);
488     PresumedLoc Loc =
489         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
490     AvailabilityInfo Availability = getAvailability(Decl);
491     DocComment Comment;
492     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
493       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
494                                               Context.getDiagnostics());
495 
496     // Build declaration fragments and sub-heading for the protocol.
497     DeclarationFragments Declaration =
498         DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
499     DeclarationFragments SubHeading =
500         DeclarationFragmentsBuilder::getSubHeading(Decl);
501 
502     ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
503         Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
504 
505     recordObjCMethods(ObjCProtocolRecord, Decl->methods());
506     recordObjCProperties(ObjCProtocolRecord, Decl->properties());
507     recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
508 
509     return true;
510   }
511 
512   bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
513     // Skip ObjC Type Parameter for now.
514     if (isa<ObjCTypeParamDecl>(Decl))
515       return true;
516 
517     if (!Decl->isDefinedOutsideFunctionOrMethod())
518       return true;
519 
520     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
521       return true;
522 
523     PresumedLoc Loc =
524         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
525     StringRef Name = Decl->getName();
526     AvailabilityInfo Availability = getAvailability(Decl);
527     StringRef USR = API.recordUSR(Decl);
528     DocComment Comment;
529     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
530       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
531                                               Context.getDiagnostics());
532 
533     QualType Type = Decl->getUnderlyingType();
534     SymbolReference SymRef =
535         TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
536                                                                          API);
537 
538     API.addTypedef(Name, USR, Loc, Availability, Comment,
539                    DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
540                    DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
541 
542     return true;
543   }
544 
545   bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
546     // Collect symbol information.
547     StringRef Name = Decl->getName();
548     StringRef USR = API.recordUSR(Decl);
549     PresumedLoc Loc =
550         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
551     AvailabilityInfo Availability = getAvailability(Decl);
552     DocComment Comment;
553     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
554       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
555                                               Context.getDiagnostics());
556     // Build declaration fragments and sub-heading for the category.
557     DeclarationFragments Declaration =
558         DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
559     DeclarationFragments SubHeading =
560         DeclarationFragmentsBuilder::getSubHeading(Decl);
561 
562     const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
563     SymbolReference Interface(InterfaceDecl->getName(),
564                               API.recordUSR(InterfaceDecl));
565 
566     ObjCCategoryRecord *ObjCCategoryRecord =
567         API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration,
568                             SubHeading, Interface);
569 
570     recordObjCMethods(ObjCCategoryRecord, Decl->methods());
571     recordObjCProperties(ObjCCategoryRecord, Decl->properties());
572     recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
573     recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
574 
575     return true;
576   }
577 
578 private:
579   /// Get availability information of the declaration \p D.
580   AvailabilityInfo getAvailability(const Decl *D) const {
581     StringRef PlatformName = Context.getTargetInfo().getPlatformName();
582 
583     AvailabilityInfo Availability;
584     // Collect availability attributes from all redeclarations.
585     for (const auto *RD : D->redecls()) {
586       for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
587         if (A->getPlatform()->getName() != PlatformName)
588           continue;
589         Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
590                                         A->getObsoleted(), A->getUnavailable(),
591                                         /* UnconditionallyDeprecated */ false,
592                                         /* UnconditionallyUnavailable */ false);
593         break;
594       }
595 
596       if (const auto *A = RD->getAttr<UnavailableAttr>())
597         if (!A->isImplicit()) {
598           Availability.Unavailable = true;
599           Availability.UnconditionallyUnavailable = true;
600         }
601 
602       if (const auto *A = RD->getAttr<DeprecatedAttr>())
603         if (!A->isImplicit())
604           Availability.UnconditionallyDeprecated = true;
605     }
606 
607     return Availability;
608   }
609 
610   /// Collect API information for the enum constants and associate with the
611   /// parent enum.
612   void recordEnumConstants(EnumRecord *EnumRecord,
613                            const EnumDecl::enumerator_range Constants) {
614     for (const auto *Constant : Constants) {
615       // Collect symbol information.
616       StringRef Name = Constant->getName();
617       StringRef USR = API.recordUSR(Constant);
618       PresumedLoc Loc =
619           Context.getSourceManager().getPresumedLoc(Constant->getLocation());
620       AvailabilityInfo Availability = getAvailability(Constant);
621       DocComment Comment;
622       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
623         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
624                                                 Context.getDiagnostics());
625 
626       // Build declaration fragments and sub-heading for the enum constant.
627       DeclarationFragments Declaration =
628           DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
629       DeclarationFragments SubHeading =
630           DeclarationFragmentsBuilder::getSubHeading(Constant);
631 
632       API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
633                           Declaration, SubHeading);
634     }
635   }
636 
637   /// Collect API information for the struct fields and associate with the
638   /// parent struct.
639   void recordStructFields(StructRecord *StructRecord,
640                           const RecordDecl::field_range Fields) {
641     for (const auto *Field : Fields) {
642       // Collect symbol information.
643       StringRef Name = Field->getName();
644       StringRef USR = API.recordUSR(Field);
645       PresumedLoc Loc =
646           Context.getSourceManager().getPresumedLoc(Field->getLocation());
647       AvailabilityInfo Availability = getAvailability(Field);
648       DocComment Comment;
649       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
650         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
651                                                 Context.getDiagnostics());
652 
653       // Build declaration fragments and sub-heading for the struct field.
654       DeclarationFragments Declaration =
655           DeclarationFragmentsBuilder::getFragmentsForField(Field);
656       DeclarationFragments SubHeading =
657           DeclarationFragmentsBuilder::getSubHeading(Field);
658 
659       API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
660                          Declaration, SubHeading);
661     }
662   }
663 
664   /// Collect API information for the Objective-C methods and associate with the
665   /// parent container.
666   void recordObjCMethods(ObjCContainerRecord *Container,
667                          const ObjCContainerDecl::method_range Methods) {
668     for (const auto *Method : Methods) {
669       // Don't record selectors for properties.
670       if (Method->isPropertyAccessor())
671         continue;
672 
673       StringRef Name = API.copyString(Method->getSelector().getAsString());
674       StringRef USR = API.recordUSR(Method);
675       PresumedLoc Loc =
676           Context.getSourceManager().getPresumedLoc(Method->getLocation());
677       AvailabilityInfo Availability = getAvailability(Method);
678       DocComment Comment;
679       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
680         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
681                                                 Context.getDiagnostics());
682 
683       // Build declaration fragments, sub-heading, and signature for the method.
684       DeclarationFragments Declaration =
685           DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
686       DeclarationFragments SubHeading =
687           DeclarationFragmentsBuilder::getSubHeading(Method);
688       FunctionSignature Signature =
689           DeclarationFragmentsBuilder::getFunctionSignature(Method);
690 
691       API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
692                         Declaration, SubHeading, Signature,
693                         Method->isInstanceMethod());
694     }
695   }
696 
697   void recordObjCProperties(ObjCContainerRecord *Container,
698                             const ObjCContainerDecl::prop_range Properties) {
699     for (const auto *Property : Properties) {
700       StringRef Name = Property->getName();
701       StringRef USR = API.recordUSR(Property);
702       PresumedLoc Loc =
703           Context.getSourceManager().getPresumedLoc(Property->getLocation());
704       AvailabilityInfo Availability = getAvailability(Property);
705       DocComment Comment;
706       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
707         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
708                                                 Context.getDiagnostics());
709 
710       // Build declaration fragments and sub-heading for the property.
711       DeclarationFragments Declaration =
712           DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
713       DeclarationFragments SubHeading =
714           DeclarationFragmentsBuilder::getSubHeading(Property);
715 
716       StringRef GetterName =
717           API.copyString(Property->getGetterName().getAsString());
718       StringRef SetterName =
719           API.copyString(Property->getSetterName().getAsString());
720 
721       // Get the attributes for property.
722       unsigned Attributes = ObjCPropertyRecord::NoAttr;
723       if (Property->getPropertyAttributes() &
724           ObjCPropertyAttribute::kind_readonly)
725         Attributes |= ObjCPropertyRecord::ReadOnly;
726       if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
727         Attributes |= ObjCPropertyRecord::Class;
728 
729       API.addObjCProperty(
730           Container, Name, USR, Loc, Availability, Comment, Declaration,
731           SubHeading,
732           static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
733           GetterName, SetterName, Property->isOptional());
734     }
735   }
736 
737   void recordObjCInstanceVariables(
738       ObjCContainerRecord *Container,
739       const llvm::iterator_range<
740           DeclContext::specific_decl_iterator<ObjCIvarDecl>>
741           Ivars) {
742     for (const auto *Ivar : Ivars) {
743       StringRef Name = Ivar->getName();
744       StringRef USR = API.recordUSR(Ivar);
745       PresumedLoc Loc =
746           Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
747       AvailabilityInfo Availability = getAvailability(Ivar);
748       DocComment Comment;
749       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
750         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
751                                                 Context.getDiagnostics());
752 
753       // Build declaration fragments and sub-heading for the instance variable.
754       DeclarationFragments Declaration =
755           DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
756       DeclarationFragments SubHeading =
757           DeclarationFragmentsBuilder::getSubHeading(Ivar);
758 
759       ObjCInstanceVariableRecord::AccessControl Access =
760           Ivar->getCanonicalAccessControl();
761 
762       API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
763                                   Comment, Declaration, SubHeading, Access);
764     }
765   }
766 
767   void recordObjCProtocols(ObjCContainerRecord *Container,
768                            ObjCInterfaceDecl::protocol_range Protocols) {
769     for (const auto *Protocol : Protocols)
770       Container->Protocols.emplace_back(Protocol->getName(),
771                                         API.recordUSR(Protocol));
772   }
773 
774   ASTContext &Context;
775   APISet &API;
776   LocationFileChecker &LCF;
777 };
778 
779 class ExtractAPIConsumer : public ASTConsumer {
780 public:
781   ExtractAPIConsumer(ASTContext &Context,
782                      std::unique_ptr<LocationFileChecker> LCF, APISet &API)
783       : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {}
784 
785   void HandleTranslationUnit(ASTContext &Context) override {
786     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
787     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
788   }
789 
790 private:
791   ExtractAPIVisitor Visitor;
792   std::unique_ptr<LocationFileChecker> LCF;
793 };
794 
795 class MacroCallback : public PPCallbacks {
796 public:
797   MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API,
798                 Preprocessor &PP)
799       : SM(SM), LCF(LCF), API(API), PP(PP) {}
800 
801   void MacroDefined(const Token &MacroNameToken,
802                     const MacroDirective *MD) override {
803     auto *MacroInfo = MD->getMacroInfo();
804 
805     if (MacroInfo->isBuiltinMacro())
806       return;
807 
808     auto SourceLoc = MacroNameToken.getLocation();
809     if (SM.isWrittenInBuiltinFile(SourceLoc) ||
810         SM.isWrittenInCommandLineFile(SourceLoc))
811       return;
812 
813     PendingMacros.emplace_back(MacroNameToken, MD);
814   }
815 
816   // If a macro gets undefined at some point during preprocessing of the inputs
817   // it means that it isn't an exposed API and we should therefore not add a
818   // macro definition for it.
819   void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
820                       const MacroDirective *Undef) override {
821     // If this macro wasn't previously defined we don't need to do anything
822     // here.
823     if (!Undef)
824       return;
825 
826     llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
827       return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
828                                               /*Syntactically*/ false);
829     });
830   }
831 
832   void EndOfMainFile() override {
833     for (auto &PM : PendingMacros) {
834       // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
835       // file so check for it here.
836       if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
837         continue;
838 
839       if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation()))
840         continue;
841 
842       StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
843       PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
844       StringRef USR =
845           API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
846 
847       API.addMacroDefinition(
848           Name, USR, Loc,
849           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
850           DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
851     }
852 
853     PendingMacros.clear();
854   }
855 
856 private:
857   struct PendingMacro {
858     Token MacroNameToken;
859     const MacroDirective *MD;
860 
861     PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
862         : MacroNameToken(MacroNameToken), MD(MD) {}
863   };
864 
865   const SourceManager &SM;
866   LocationFileChecker &LCF;
867   APISet &API;
868   Preprocessor &PP;
869   llvm::SmallVector<PendingMacro> PendingMacros;
870 };
871 
872 } // namespace
873 
874 std::unique_ptr<ASTConsumer>
875 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
876   OS = CreateOutputFile(CI, InFile);
877   if (!OS)
878     return nullptr;
879 
880   ProductName = CI.getFrontendOpts().ProductName;
881 
882   // Now that we have enough information about the language options and the
883   // target triple, let's create the APISet before anyone uses it.
884   API = std::make_unique<APISet>(
885       CI.getTarget().getTriple(),
886       CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
887 
888   auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
889 
890   CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
891       CI.getSourceManager(), *LCF, *API, CI.getPreprocessor()));
892 
893   return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
894                                               std::move(LCF), *API);
895 }
896 
897 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
898   auto &Inputs = CI.getFrontendOpts().Inputs;
899   if (Inputs.empty())
900     return true;
901 
902   if (!CI.hasFileManager())
903     if (!CI.createFileManager())
904       return false;
905 
906   auto Kind = Inputs[0].getKind();
907 
908   // Convert the header file inputs into a single input buffer.
909   SmallString<256> HeaderContents;
910   bool IsQuoted = false;
911   for (const FrontendInputFile &FIF : Inputs) {
912     if (Kind.isObjectiveC())
913       HeaderContents += "#import";
914     else
915       HeaderContents += "#include";
916 
917     StringRef FilePath = FIF.getFile();
918     if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
919       if (IsQuoted)
920         HeaderContents += " \"";
921       else
922         HeaderContents += " <";
923 
924       HeaderContents += *RelativeName;
925 
926       if (IsQuoted)
927         HeaderContents += "\"\n";
928       else
929         HeaderContents += ">\n";
930       KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
931                                    IsQuoted);
932     } else {
933       HeaderContents += " \"";
934       HeaderContents += FilePath;
935       HeaderContents += "\"\n";
936       KnownInputFiles.emplace_back(FilePath, true);
937     }
938   }
939 
940   if (CI.getHeaderSearchOpts().Verbose)
941     CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
942                                 << HeaderContents << "\n";
943 
944   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
945                                                 getInputBufferName());
946 
947   // Set that buffer up as our "real" input in the CompilerInstance.
948   Inputs.clear();
949   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
950 
951   return true;
952 }
953 
954 void ExtractAPIAction::EndSourceFileAction() {
955   if (!OS)
956     return;
957 
958   // Setup a SymbolGraphSerializer to write out collected API information in
959   // the Symbol Graph format.
960   // FIXME: Make the kind of APISerializer configurable.
961   SymbolGraphSerializer SGSerializer(*API, ProductName);
962   SGSerializer.serialize(*OS);
963   OS.reset();
964 }
965 
966 std::unique_ptr<raw_pwrite_stream>
967 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
968   std::unique_ptr<raw_pwrite_stream> OS =
969       CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
970                                  /*RemoveFileOnSignal=*/false);
971   if (!OS)
972     return nullptr;
973   return OS;
974 }
975