1 //===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
10 /// This file implements the ExtractAPIAction, and ASTVisitor/Consumer to
11 /// collect API information.
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"
46 using namespace clang
;
47 using namespace extractapi
;
51 StringRef
getTypedefName(const TagDecl
*Decl
) {
52 if (const auto *TypedefDecl
= Decl
->getTypedefNameForAnonDecl())
53 return TypedefDecl
->getName();
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
);
67 const auto *File
= SM
.getFileEntryForID(FID
);
71 if (KnownFileEntries
.count(File
))
77 LocationFileChecker(const SourceManager
&SM
,
78 const std::vector
<std::string
> &KnownFiles
)
80 for (const auto &KnownFilePath
: KnownFiles
)
81 if (auto FileEntry
= SM
.getFileManager().getFile(KnownFilePath
))
82 KnownFileEntries
.insert(*FileEntry
);
86 const SourceManager
&SM
;
87 llvm::DenseSet
<const FileEntry
*> KnownFileEntries
;
90 /// The RecursiveASTVisitor to traverse symbol declarations and collect API
92 class ExtractAPIVisitor
: public RecursiveASTVisitor
<ExtractAPIVisitor
> {
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
))
104 // Skip non-global variables in records (struct/union/class).
105 if (Decl
->getDeclContext()->isRecord())
108 // Skip local variables inside function or method.
109 if (!Decl
->isDefinedOutsideFunctionOrMethod())
112 // If this is a template but not specialization or instantiation, skip.
113 if (Decl
->getASTContext().getTemplateOrSpecializationInfo(Decl
) &&
114 Decl
->getTemplateSpecializationKind() == TSK_Undeclared
)
117 if (!LCF
.isLocationInKnownFile(Decl
->getLocation()))
120 // Collect symbol information.
121 StringRef Name
= Decl
->getName();
122 StringRef USR
= API
.recordUSR(Decl
);
124 Context
.getSourceManager().getPresumedLoc(Decl
->getLocation());
125 AvailabilityInfo Availability
= getAvailability(Decl
);
126 LinkageInfo Linkage
= Decl
->getLinkageAndVisibility();
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
);
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)
150 // Skip methods in records.
151 for (auto P
: Context
.getParents(*Method
)) {
152 if (P
.get
<CXXRecordDecl
>())
156 // Skip ConstructorDecl and DestructorDecl.
157 if (isa
<CXXConstructorDecl
>(Method
) || isa
<CXXDestructorDecl
>(Method
))
161 // Skip templated functions.
162 switch (Decl
->getTemplatedKind()) {
163 case FunctionDecl::TK_NonTemplate
:
165 case FunctionDecl::TK_MemberSpecialization
:
166 case FunctionDecl::TK_FunctionTemplateSpecialization
:
167 if (auto *TemplateInfo
= Decl
->getTemplateSpecializationInfo()) {
168 if (!TemplateInfo
->isExplicitInstantiationOrSpecialization())
172 case FunctionDecl::TK_FunctionTemplate
:
173 case FunctionDecl::TK_DependentFunctionTemplateSpecialization
:
177 if (!LCF
.isLocationInKnownFile(Decl
->getLocation()))
180 // Collect symbol information.
181 StringRef Name
= Decl
->getName();
182 StringRef USR
= API
.recordUSR(Decl
);
184 Context
.getSourceManager().getPresumedLoc(Decl
->getLocation());
185 AvailabilityInfo Availability
= getAvailability(Decl
);
186 LinkageInfo Linkage
= Decl
->getLinkageAndVisibility();
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
);
206 bool VisitEnumDecl(const EnumDecl
*Decl
) {
207 if (!Decl
->isComplete())
210 // Skip forward declaration.
211 if (!Decl
->isThisDeclarationADefinition())
214 if (!LCF
.isLocationInKnownFile(Decl
->getLocation()))
217 // Collect symbol information.
218 StringRef Name
= Decl
->getName();
220 Name
= getTypedefName(Decl
);
221 StringRef USR
= API
.recordUSR(Decl
);
223 Context
.getSourceManager().getPresumedLoc(Decl
->getLocation());
224 AvailabilityInfo Availability
= getAvailability(Decl
);
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());
245 bool VisitRecordDecl(const RecordDecl
*Decl
) {
246 if (!Decl
->isCompleteDefinition())
249 // Skip C++ structs/classes/unions
250 // TODO: support C++ records
251 if (isa
<CXXRecordDecl
>(Decl
))
254 if (!LCF
.isLocationInKnownFile(Decl
->getLocation()))
257 // Collect symbol information.
258 StringRef Name
= Decl
->getName();
260 Name
= getTypedefName(Decl
);
261 StringRef USR
= API
.recordUSR(Decl
);
263 Context
.getSourceManager().getPresumedLoc(Decl
->getLocation());
264 AvailabilityInfo Availability
= getAvailability(Decl
);
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());
285 bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl
*Decl
) {
286 // Skip forward declaration for classes (@class)
287 if (!Decl
->isThisDeclarationADefinition())
290 if (!LCF
.isLocationInKnownFile(Decl
->getLocation()))
293 // Collect symbol information.
294 StringRef Name
= Decl
->getName();
295 StringRef USR
= API
.recordUSR(Decl
);
297 Context
.getSourceManager().getPresumedLoc(Decl
->getLocation());
298 AvailabilityInfo Availability
= getAvailability(Decl
);
299 LinkageInfo Linkage
= Decl
->getLinkageAndVisibility();
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());
332 bool VisitObjCProtocolDecl(const ObjCProtocolDecl
*Decl
) {
333 // Skip forward declaration for protocols (@protocol).
334 if (!Decl
->isThisDeclarationADefinition())
337 if (!LCF
.isLocationInKnownFile(Decl
->getLocation()))
340 // Collect symbol information.
341 StringRef Name
= Decl
->getName();
342 StringRef USR
= API
.recordUSR(Decl
);
344 Context
.getSourceManager().getPresumedLoc(Decl
->getLocation());
345 AvailabilityInfo Availability
= getAvailability(Decl
);
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());
367 bool VisitTypedefNameDecl(const TypedefNameDecl
*Decl
) {
368 // Skip ObjC Type Parameter for now.
369 if (isa
<ObjCTypeParamDecl
>(Decl
))
372 if (!Decl
->isDefinedOutsideFunctionOrMethod())
375 if (!LCF
.isLocationInKnownFile(Decl
->getLocation()))
379 Context
.getSourceManager().getPresumedLoc(Decl
->getLocation());
380 StringRef Name
= Decl
->getName();
381 AvailabilityInfo Availability
= getAvailability(Decl
);
382 StringRef USR
= API
.recordUSR(Decl
);
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
,
393 API
.addTypedef(Name
, USR
, Loc
, Availability
, Comment
,
394 DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl
),
395 DeclarationFragmentsBuilder::getSubHeading(Decl
), SymRef
);
400 bool VisitObjCCategoryDecl(const ObjCCategoryDecl
*Decl
) {
401 // Collect symbol information.
402 StringRef Name
= Decl
->getName();
403 StringRef USR
= API
.recordUSR(Decl
);
405 Context
.getSourceManager().getPresumedLoc(Decl
->getLocation());
406 AvailabilityInfo Availability
= getAvailability(Decl
);
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());
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
)
444 Availability
= AvailabilityInfo(A
->getIntroduced(), A
->getDeprecated(),
445 A
->getObsoleted(), A
->getUnavailable(),
446 /* UnconditionallyDeprecated */ false,
447 /* UnconditionallyUnavailable */ false);
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;
465 /// Collect API information for the enum constants and associate with the
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
);
474 Context
.getSourceManager().getPresumedLoc(Constant
->getLocation());
475 AvailabilityInfo Availability
= getAvailability(Constant
);
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
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
);
501 Context
.getSourceManager().getPresumedLoc(Field
->getLocation());
502 AvailabilityInfo Availability
= getAvailability(Field
);
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())
528 StringRef Name
= API
.copyString(Method
->getSelector().getAsString());
529 StringRef USR
= API
.recordUSR(Method
);
531 Context
.getSourceManager().getPresumedLoc(Method
->getLocation());
532 AvailabilityInfo Availability
= getAvailability(Method
);
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
);
558 Context
.getSourceManager().getPresumedLoc(Property
->getLocation());
559 AvailabilityInfo Availability
= getAvailability(Property
);
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
;
585 Container
, Name
, USR
, Loc
, Availability
, Comment
, Declaration
,
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
>>
597 for (const auto *Ivar
: Ivars
) {
598 StringRef Name
= Ivar
->getName();
599 StringRef USR
= API
.recordUSR(Ivar
);
601 Context
.getSourceManager().getPresumedLoc(Ivar
->getLocation());
602 AvailabilityInfo Availability
= getAvailability(Ivar
);
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
));
631 LocationFileChecker
&LCF
;
634 class ExtractAPIConsumer
: public ASTConsumer
{
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());
646 ExtractAPIVisitor Visitor
;
647 std::unique_ptr
<LocationFileChecker
> LCF
;
650 class MacroCallback
: public PPCallbacks
{
652 MacroCallback(const SourceManager
&SM
, LocationFileChecker
&LCF
, APISet
&API
,
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())
663 auto SourceLoc
= MacroNameToken
.getLocation();
664 if (SM
.isWrittenInBuiltinFile(SourceLoc
) ||
665 SM
.isWrittenInCommandLineFile(SourceLoc
))
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
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())
694 if (!LCF
.isLocationInKnownFile(PM
.MacroNameToken
.getLocation()))
697 StringRef Name
= PM
.MacroNameToken
.getIdentifierInfo()->getName();
698 PresumedLoc Loc
= SM
.getPresumedLoc(PM
.MacroNameToken
.getLocation());
700 API
.recordUSRForMacro(Name
, PM
.MacroNameToken
.getLocation(), SM
);
702 API
.addMacroDefinition(
704 DeclarationFragmentsBuilder::getFragmentsForMacro(Name
, PM
.MD
),
705 DeclarationFragmentsBuilder::getSubHeadingForMacro(Name
));
708 PendingMacros
.clear();
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
;
724 llvm::SmallVector
<PendingMacro
> PendingMacros
;
729 std::unique_ptr
<ASTConsumer
>
730 ExtractAPIAction::CreateASTConsumer(CompilerInstance
&CI
, StringRef InFile
) {
731 OS
= CreateOutputFile(CI
, InFile
);
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(),
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
;
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";
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.
779 Inputs
.emplace_back(Buffer
->getMemBufferRef(), Kind
, /*IsSystem*/ false);
784 void ExtractAPIAction::EndSourceFileAction() {
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
);
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);