[clang][extract-api][NFC] Use dedicated API to check for macro equality
[llvm-project.git] / clang / lib / ExtractAPI / ExtractAPIConsumer.cpp
blob075788186298446ea8a22b260a515e55eee37137
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 //===----------------------------------------------------------------------===//
15 #include "TypedefUnderlyingTypeResolver.h"
16 #include "clang/AST/ASTConsumer.h"
17 #include "clang/AST/ASTContext.h"
18 #include "clang/AST/Decl.h"
19 #include "clang/AST/DeclCXX.h"
20 #include "clang/AST/ParentMapContext.h"
21 #include "clang/AST/RawCommentList.h"
22 #include "clang/AST/RecursiveASTVisitor.h"
23 #include "clang/Basic/SourceLocation.h"
24 #include "clang/Basic/SourceManager.h"
25 #include "clang/Basic/TargetInfo.h"
26 #include "clang/ExtractAPI/API.h"
27 #include "clang/ExtractAPI/AvailabilityInfo.h"
28 #include "clang/ExtractAPI/DeclarationFragments.h"
29 #include "clang/ExtractAPI/FrontendActions.h"
30 #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
31 #include "clang/Frontend/ASTConsumers.h"
32 #include "clang/Frontend/CompilerInstance.h"
33 #include "clang/Frontend/FrontendOptions.h"
34 #include "clang/Lex/MacroInfo.h"
35 #include "clang/Lex/PPCallbacks.h"
36 #include "clang/Lex/Preprocessor.h"
37 #include "clang/Lex/PreprocessorOptions.h"
38 #include "llvm/ADT/DenseSet.h"
39 #include "llvm/ADT/STLExtras.h"
40 #include "llvm/ADT/SmallVector.h"
41 #include "llvm/Support/MemoryBuffer.h"
42 #include "llvm/Support/raw_ostream.h"
43 #include <memory>
44 #include <utility>
46 using namespace clang;
47 using namespace extractapi;
49 namespace {
51 StringRef getTypedefName(const TagDecl *Decl) {
52 if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
53 return TypedefDecl->getName();
55 return {};
58 struct LocationFileChecker {
59 bool isLocationInKnownFile(SourceLocation Loc) {
60 // If the loc refers to a macro expansion we need to first get the file
61 // location of the expansion.
62 auto FileLoc = SM.getFileLoc(Loc);
63 FileID FID = SM.getFileID(FileLoc);
64 if (FID.isInvalid())
65 return false;
67 const auto *File = SM.getFileEntryForID(FID);
68 if (!File)
69 return false;
71 if (KnownFileEntries.count(File))
72 return true;
74 return false;
77 LocationFileChecker(const SourceManager &SM,
78 const std::vector<std::string> &KnownFiles)
79 : SM(SM) {
80 for (const auto &KnownFilePath : KnownFiles)
81 if (auto FileEntry = SM.getFileManager().getFile(KnownFilePath))
82 KnownFileEntries.insert(*FileEntry);
85 private:
86 const SourceManager &SM;
87 llvm::DenseSet<const FileEntry *> KnownFileEntries;
90 /// The RecursiveASTVisitor to traverse symbol declarations and collect API
91 /// information.
92 class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
93 public:
94 ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API)
95 : Context(Context), API(API), LCF(LCF) {}
97 const APISet &getAPI() const { return API; }
99 bool VisitVarDecl(const VarDecl *Decl) {
100 // Skip function parameters.
101 if (isa<ParmVarDecl>(Decl))
102 return true;
104 // Skip non-global variables in records (struct/union/class).
105 if (Decl->getDeclContext()->isRecord())
106 return true;
108 // Skip local variables inside function or method.
109 if (!Decl->isDefinedOutsideFunctionOrMethod())
110 return true;
112 // If this is a template but not specialization or instantiation, skip.
113 if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
114 Decl->getTemplateSpecializationKind() == TSK_Undeclared)
115 return true;
117 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
118 return true;
120 // Collect symbol information.
121 StringRef Name = Decl->getName();
122 StringRef USR = API.recordUSR(Decl);
123 PresumedLoc Loc =
124 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
125 AvailabilityInfo Availability = getAvailability(Decl);
126 LinkageInfo Linkage = Decl->getLinkageAndVisibility();
127 DocComment Comment;
128 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
129 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
130 Context.getDiagnostics());
132 // Build declaration fragments and sub-heading for the variable.
133 DeclarationFragments Declaration =
134 DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
135 DeclarationFragments SubHeading =
136 DeclarationFragmentsBuilder::getSubHeading(Decl);
138 // Add the global variable record to the API set.
139 API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment,
140 Declaration, SubHeading);
141 return true;
144 bool VisitFunctionDecl(const FunctionDecl *Decl) {
145 if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
146 // Skip member function in class templates.
147 if (Method->getParent()->getDescribedClassTemplate() != nullptr)
148 return true;
150 // Skip methods in records.
151 for (auto P : Context.getParents(*Method)) {
152 if (P.get<CXXRecordDecl>())
153 return true;
156 // Skip ConstructorDecl and DestructorDecl.
157 if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
158 return true;
161 // Skip templated functions.
162 switch (Decl->getTemplatedKind()) {
163 case FunctionDecl::TK_NonTemplate:
164 break;
165 case FunctionDecl::TK_MemberSpecialization:
166 case FunctionDecl::TK_FunctionTemplateSpecialization:
167 if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
168 if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
169 return true;
171 break;
172 case FunctionDecl::TK_FunctionTemplate:
173 case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
174 return true;
177 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
178 return true;
180 // Collect symbol information.
181 StringRef Name = Decl->getName();
182 StringRef USR = API.recordUSR(Decl);
183 PresumedLoc Loc =
184 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
185 AvailabilityInfo Availability = getAvailability(Decl);
186 LinkageInfo Linkage = Decl->getLinkageAndVisibility();
187 DocComment Comment;
188 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
189 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
190 Context.getDiagnostics());
192 // Build declaration fragments, sub-heading, and signature of the function.
193 DeclarationFragments Declaration =
194 DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
195 DeclarationFragments SubHeading =
196 DeclarationFragmentsBuilder::getSubHeading(Decl);
197 FunctionSignature Signature =
198 DeclarationFragmentsBuilder::getFunctionSignature(Decl);
200 // Add the function record to the API set.
201 API.addFunction(Name, USR, Loc, Availability, Linkage, Comment, Declaration,
202 SubHeading, Signature);
203 return true;
206 bool VisitEnumDecl(const EnumDecl *Decl) {
207 if (!Decl->isComplete())
208 return true;
210 // Skip forward declaration.
211 if (!Decl->isThisDeclarationADefinition())
212 return true;
214 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
215 return true;
217 // Collect symbol information.
218 StringRef Name = Decl->getName();
219 if (Name.empty())
220 Name = getTypedefName(Decl);
221 StringRef USR = API.recordUSR(Decl);
222 PresumedLoc Loc =
223 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
224 AvailabilityInfo Availability = getAvailability(Decl);
225 DocComment Comment;
226 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
227 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
228 Context.getDiagnostics());
230 // Build declaration fragments and sub-heading for the enum.
231 DeclarationFragments Declaration =
232 DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
233 DeclarationFragments SubHeading =
234 DeclarationFragmentsBuilder::getSubHeading(Decl);
236 EnumRecord *EnumRecord = API.addEnum(Name, USR, Loc, Availability, Comment,
237 Declaration, SubHeading);
239 // Now collect information about the enumerators in this enum.
240 recordEnumConstants(EnumRecord, Decl->enumerators());
242 return true;
245 bool VisitRecordDecl(const RecordDecl *Decl) {
246 if (!Decl->isCompleteDefinition())
247 return true;
249 // Skip C++ structs/classes/unions
250 // TODO: support C++ records
251 if (isa<CXXRecordDecl>(Decl))
252 return true;
254 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
255 return true;
257 // Collect symbol information.
258 StringRef Name = Decl->getName();
259 if (Name.empty())
260 Name = getTypedefName(Decl);
261 StringRef USR = API.recordUSR(Decl);
262 PresumedLoc Loc =
263 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
264 AvailabilityInfo Availability = getAvailability(Decl);
265 DocComment Comment;
266 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
267 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
268 Context.getDiagnostics());
270 // Build declaration fragments and sub-heading for the struct.
271 DeclarationFragments Declaration =
272 DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
273 DeclarationFragments SubHeading =
274 DeclarationFragmentsBuilder::getSubHeading(Decl);
276 StructRecord *StructRecord = API.addStruct(
277 Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
279 // Now collect information about the fields in this struct.
280 recordStructFields(StructRecord, Decl->fields());
282 return true;
285 bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
286 // Skip forward declaration for classes (@class)
287 if (!Decl->isThisDeclarationADefinition())
288 return true;
290 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
291 return true;
293 // Collect symbol information.
294 StringRef Name = Decl->getName();
295 StringRef USR = API.recordUSR(Decl);
296 PresumedLoc Loc =
297 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
298 AvailabilityInfo Availability = getAvailability(Decl);
299 LinkageInfo Linkage = Decl->getLinkageAndVisibility();
300 DocComment Comment;
301 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
302 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
303 Context.getDiagnostics());
305 // Build declaration fragments and sub-heading for the interface.
306 DeclarationFragments Declaration =
307 DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
308 DeclarationFragments SubHeading =
309 DeclarationFragmentsBuilder::getSubHeading(Decl);
311 // Collect super class information.
312 SymbolReference SuperClass;
313 if (const auto *SuperClassDecl = Decl->getSuperClass()) {
314 SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
315 SuperClass.USR = API.recordUSR(SuperClassDecl);
318 ObjCInterfaceRecord *ObjCInterfaceRecord =
319 API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment,
320 Declaration, SubHeading, SuperClass);
322 // Record all methods (selectors). This doesn't include automatically
323 // synthesized property methods.
324 recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
325 recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
326 recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
327 recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
329 return true;
332 bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
333 // Skip forward declaration for protocols (@protocol).
334 if (!Decl->isThisDeclarationADefinition())
335 return true;
337 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
338 return true;
340 // Collect symbol information.
341 StringRef Name = Decl->getName();
342 StringRef USR = API.recordUSR(Decl);
343 PresumedLoc Loc =
344 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
345 AvailabilityInfo Availability = getAvailability(Decl);
346 DocComment Comment;
347 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
348 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
349 Context.getDiagnostics());
351 // Build declaration fragments and sub-heading for the protocol.
352 DeclarationFragments Declaration =
353 DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
354 DeclarationFragments SubHeading =
355 DeclarationFragmentsBuilder::getSubHeading(Decl);
357 ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
358 Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
360 recordObjCMethods(ObjCProtocolRecord, Decl->methods());
361 recordObjCProperties(ObjCProtocolRecord, Decl->properties());
362 recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
364 return true;
367 bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
368 // Skip ObjC Type Parameter for now.
369 if (isa<ObjCTypeParamDecl>(Decl))
370 return true;
372 if (!Decl->isDefinedOutsideFunctionOrMethod())
373 return true;
375 if (!LCF.isLocationInKnownFile(Decl->getLocation()))
376 return true;
378 PresumedLoc Loc =
379 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
380 StringRef Name = Decl->getName();
381 AvailabilityInfo Availability = getAvailability(Decl);
382 StringRef USR = API.recordUSR(Decl);
383 DocComment Comment;
384 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
385 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
386 Context.getDiagnostics());
388 QualType Type = Decl->getUnderlyingType();
389 SymbolReference SymRef =
390 TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
391 API);
393 API.addTypedef(Name, USR, Loc, Availability, Comment,
394 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
395 DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
397 return true;
400 bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
401 // Collect symbol information.
402 StringRef Name = Decl->getName();
403 StringRef USR = API.recordUSR(Decl);
404 PresumedLoc Loc =
405 Context.getSourceManager().getPresumedLoc(Decl->getLocation());
406 AvailabilityInfo Availability = getAvailability(Decl);
407 DocComment Comment;
408 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
409 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
410 Context.getDiagnostics());
411 // Build declaration fragments and sub-heading for the category.
412 DeclarationFragments Declaration =
413 DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
414 DeclarationFragments SubHeading =
415 DeclarationFragmentsBuilder::getSubHeading(Decl);
417 const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
418 SymbolReference Interface(InterfaceDecl->getName(),
419 API.recordUSR(InterfaceDecl));
421 ObjCCategoryRecord *ObjCCategoryRecord =
422 API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration,
423 SubHeading, Interface);
425 recordObjCMethods(ObjCCategoryRecord, Decl->methods());
426 recordObjCProperties(ObjCCategoryRecord, Decl->properties());
427 recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
428 recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
430 return true;
433 private:
434 /// Get availability information of the declaration \p D.
435 AvailabilityInfo getAvailability(const Decl *D) const {
436 StringRef PlatformName = Context.getTargetInfo().getPlatformName();
438 AvailabilityInfo Availability;
439 // Collect availability attributes from all redeclarations.
440 for (const auto *RD : D->redecls()) {
441 for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
442 if (A->getPlatform()->getName() != PlatformName)
443 continue;
444 Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
445 A->getObsoleted(), A->getUnavailable(),
446 /* UnconditionallyDeprecated */ false,
447 /* UnconditionallyUnavailable */ false);
448 break;
451 if (const auto *A = RD->getAttr<UnavailableAttr>())
452 if (!A->isImplicit()) {
453 Availability.Unavailable = true;
454 Availability.UnconditionallyUnavailable = true;
457 if (const auto *A = RD->getAttr<DeprecatedAttr>())
458 if (!A->isImplicit())
459 Availability.UnconditionallyDeprecated = true;
462 return Availability;
465 /// Collect API information for the enum constants and associate with the
466 /// parent enum.
467 void recordEnumConstants(EnumRecord *EnumRecord,
468 const EnumDecl::enumerator_range Constants) {
469 for (const auto *Constant : Constants) {
470 // Collect symbol information.
471 StringRef Name = Constant->getName();
472 StringRef USR = API.recordUSR(Constant);
473 PresumedLoc Loc =
474 Context.getSourceManager().getPresumedLoc(Constant->getLocation());
475 AvailabilityInfo Availability = getAvailability(Constant);
476 DocComment Comment;
477 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
478 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
479 Context.getDiagnostics());
481 // Build declaration fragments and sub-heading for the enum constant.
482 DeclarationFragments Declaration =
483 DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
484 DeclarationFragments SubHeading =
485 DeclarationFragmentsBuilder::getSubHeading(Constant);
487 API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
488 Declaration, SubHeading);
492 /// Collect API information for the struct fields and associate with the
493 /// parent struct.
494 void recordStructFields(StructRecord *StructRecord,
495 const RecordDecl::field_range Fields) {
496 for (const auto *Field : Fields) {
497 // Collect symbol information.
498 StringRef Name = Field->getName();
499 StringRef USR = API.recordUSR(Field);
500 PresumedLoc Loc =
501 Context.getSourceManager().getPresumedLoc(Field->getLocation());
502 AvailabilityInfo Availability = getAvailability(Field);
503 DocComment Comment;
504 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
505 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
506 Context.getDiagnostics());
508 // Build declaration fragments and sub-heading for the struct field.
509 DeclarationFragments Declaration =
510 DeclarationFragmentsBuilder::getFragmentsForField(Field);
511 DeclarationFragments SubHeading =
512 DeclarationFragmentsBuilder::getSubHeading(Field);
514 API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
515 Declaration, SubHeading);
519 /// Collect API information for the Objective-C methods and associate with the
520 /// parent container.
521 void recordObjCMethods(ObjCContainerRecord *Container,
522 const ObjCContainerDecl::method_range Methods) {
523 for (const auto *Method : Methods) {
524 // Don't record selectors for properties.
525 if (Method->isPropertyAccessor())
526 continue;
528 StringRef Name = API.copyString(Method->getSelector().getAsString());
529 StringRef USR = API.recordUSR(Method);
530 PresumedLoc Loc =
531 Context.getSourceManager().getPresumedLoc(Method->getLocation());
532 AvailabilityInfo Availability = getAvailability(Method);
533 DocComment Comment;
534 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
535 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
536 Context.getDiagnostics());
538 // Build declaration fragments, sub-heading, and signature for the method.
539 DeclarationFragments Declaration =
540 DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
541 DeclarationFragments SubHeading =
542 DeclarationFragmentsBuilder::getSubHeading(Method);
543 FunctionSignature Signature =
544 DeclarationFragmentsBuilder::getFunctionSignature(Method);
546 API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
547 Declaration, SubHeading, Signature,
548 Method->isInstanceMethod());
552 void recordObjCProperties(ObjCContainerRecord *Container,
553 const ObjCContainerDecl::prop_range Properties) {
554 for (const auto *Property : Properties) {
555 StringRef Name = Property->getName();
556 StringRef USR = API.recordUSR(Property);
557 PresumedLoc Loc =
558 Context.getSourceManager().getPresumedLoc(Property->getLocation());
559 AvailabilityInfo Availability = getAvailability(Property);
560 DocComment Comment;
561 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
562 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
563 Context.getDiagnostics());
565 // Build declaration fragments and sub-heading for the property.
566 DeclarationFragments Declaration =
567 DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
568 DeclarationFragments SubHeading =
569 DeclarationFragmentsBuilder::getSubHeading(Property);
571 StringRef GetterName =
572 API.copyString(Property->getGetterName().getAsString());
573 StringRef SetterName =
574 API.copyString(Property->getSetterName().getAsString());
576 // Get the attributes for property.
577 unsigned Attributes = ObjCPropertyRecord::NoAttr;
578 if (Property->getPropertyAttributes() &
579 ObjCPropertyAttribute::kind_readonly)
580 Attributes |= ObjCPropertyRecord::ReadOnly;
581 if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
582 Attributes |= ObjCPropertyRecord::Class;
584 API.addObjCProperty(
585 Container, Name, USR, Loc, Availability, Comment, Declaration,
586 SubHeading,
587 static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
588 GetterName, SetterName, Property->isOptional());
592 void recordObjCInstanceVariables(
593 ObjCContainerRecord *Container,
594 const llvm::iterator_range<
595 DeclContext::specific_decl_iterator<ObjCIvarDecl>>
596 Ivars) {
597 for (const auto *Ivar : Ivars) {
598 StringRef Name = Ivar->getName();
599 StringRef USR = API.recordUSR(Ivar);
600 PresumedLoc Loc =
601 Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
602 AvailabilityInfo Availability = getAvailability(Ivar);
603 DocComment Comment;
604 if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
605 Comment = RawComment->getFormattedLines(Context.getSourceManager(),
606 Context.getDiagnostics());
608 // Build declaration fragments and sub-heading for the instance variable.
609 DeclarationFragments Declaration =
610 DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
611 DeclarationFragments SubHeading =
612 DeclarationFragmentsBuilder::getSubHeading(Ivar);
614 ObjCInstanceVariableRecord::AccessControl Access =
615 Ivar->getCanonicalAccessControl();
617 API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
618 Comment, Declaration, SubHeading, Access);
622 void recordObjCProtocols(ObjCContainerRecord *Container,
623 ObjCInterfaceDecl::protocol_range Protocols) {
624 for (const auto *Protocol : Protocols)
625 Container->Protocols.emplace_back(Protocol->getName(),
626 API.recordUSR(Protocol));
629 ASTContext &Context;
630 APISet &API;
631 LocationFileChecker &LCF;
634 class ExtractAPIConsumer : public ASTConsumer {
635 public:
636 ExtractAPIConsumer(ASTContext &Context,
637 std::unique_ptr<LocationFileChecker> LCF, APISet &API)
638 : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {}
640 void HandleTranslationUnit(ASTContext &Context) override {
641 // Use ExtractAPIVisitor to traverse symbol declarations in the context.
642 Visitor.TraverseDecl(Context.getTranslationUnitDecl());
645 private:
646 ExtractAPIVisitor Visitor;
647 std::unique_ptr<LocationFileChecker> LCF;
650 class MacroCallback : public PPCallbacks {
651 public:
652 MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API,
653 Preprocessor &PP)
654 : SM(SM), LCF(LCF), API(API), PP(PP) {}
656 void MacroDefined(const Token &MacroNameToken,
657 const MacroDirective *MD) override {
658 auto *MacroInfo = MD->getMacroInfo();
660 if (MacroInfo->isBuiltinMacro())
661 return;
663 auto SourceLoc = MacroNameToken.getLocation();
664 if (SM.isWrittenInBuiltinFile(SourceLoc) ||
665 SM.isWrittenInCommandLineFile(SourceLoc))
666 return;
668 PendingMacros.emplace_back(MacroNameToken, MD);
671 // If a macro gets undefined at some point during preprocessing of the inputs
672 // it means that it isn't an exposed API and we should therefore not add a
673 // macro definition for it.
674 void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
675 const MacroDirective *Undef) override {
676 // If this macro wasn't previously defined we don't need to do anything
677 // here.
678 if (!Undef)
679 return;
681 llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
682 return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
683 /*Syntactically*/ false);
687 void EndOfMainFile() override {
688 for (auto &PM : PendingMacros) {
689 // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
690 // file so check for it here.
691 if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
692 continue;
694 if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation()))
695 continue;
697 StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
698 PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
699 StringRef USR =
700 API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
702 API.addMacroDefinition(
703 Name, USR, Loc,
704 DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
705 DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
708 PendingMacros.clear();
711 private:
712 struct PendingMacro {
713 Token MacroNameToken;
714 const MacroDirective *MD;
716 PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
717 : MacroNameToken(MacroNameToken), MD(MD) {}
720 const SourceManager &SM;
721 LocationFileChecker &LCF;
722 APISet &API;
723 Preprocessor &PP;
724 llvm::SmallVector<PendingMacro> PendingMacros;
727 } // namespace
729 std::unique_ptr<ASTConsumer>
730 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
731 OS = CreateOutputFile(CI, InFile);
732 if (!OS)
733 return nullptr;
735 ProductName = CI.getFrontendOpts().ProductName;
737 // Now that we have enough information about the language options and the
738 // target triple, let's create the APISet before anyone uses it.
739 API = std::make_unique<APISet>(
740 CI.getTarget().getTriple(),
741 CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
743 auto LCF = std::make_unique<LocationFileChecker>(CI.getSourceManager(),
744 KnownInputFiles);
746 CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
747 CI.getSourceManager(), *LCF, *API, CI.getPreprocessor()));
749 return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
750 std::move(LCF), *API);
753 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
754 auto &Inputs = CI.getFrontendOpts().Inputs;
755 if (Inputs.empty())
756 return true;
758 auto Kind = Inputs[0].getKind();
760 // Convert the header file inputs into a single input buffer.
761 SmallString<256> HeaderContents;
762 for (const FrontendInputFile &FIF : Inputs) {
763 if (Kind.isObjectiveC())
764 HeaderContents += "#import";
765 else
766 HeaderContents += "#include";
767 HeaderContents += " \"";
768 HeaderContents += FIF.getFile();
769 HeaderContents += "\"\n";
771 KnownInputFiles.emplace_back(FIF.getFile());
774 Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
775 getInputBufferName());
777 // Set that buffer up as our "real" input in the CompilerInstance.
778 Inputs.clear();
779 Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
781 return true;
784 void ExtractAPIAction::EndSourceFileAction() {
785 if (!OS)
786 return;
788 // Setup a SymbolGraphSerializer to write out collected API information in
789 // the Symbol Graph format.
790 // FIXME: Make the kind of APISerializer configurable.
791 SymbolGraphSerializer SGSerializer(*API, ProductName);
792 SGSerializer.serialize(*OS);
793 OS->flush();
796 std::unique_ptr<raw_pwrite_stream>
797 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
798 std::unique_ptr<raw_pwrite_stream> OS =
799 CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
800 /*RemoveFileOnSignal=*/false);
801 if (!OS)
802 return nullptr;
803 return OS;