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 "clang/AST/ASTConsumer.h"
16 #include "clang/AST/ASTContext.h"
17 #include "clang/AST/Decl.h"
18 #include "clang/AST/DeclCXX.h"
19 #include "clang/AST/ParentMapContext.h"
20 #include "clang/AST/RawCommentList.h"
21 #include "clang/AST/RecursiveASTVisitor.h"
22 #include "clang/Basic/TargetInfo.h"
23 #include "clang/ExtractAPI/API.h"
24 #include "clang/ExtractAPI/AvailabilityInfo.h"
25 #include "clang/ExtractAPI/DeclarationFragments.h"
26 #include "clang/ExtractAPI/FrontendActions.h"
27 #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
28 #include "clang/Frontend/ASTConsumers.h"
29 #include "clang/Frontend/CompilerInstance.h"
30 #include "clang/Frontend/FrontendOptions.h"
31 #include "llvm/ADT/SmallVector.h"
32 #include "llvm/Support/MemoryBuffer.h"
33 #include "llvm/Support/raw_ostream.h"
34 
35 using namespace clang;
36 using namespace extractapi;
37 
38 namespace {
39 
40 /// The RecursiveASTVisitor to traverse symbol declarations and collect API
41 /// information.
42 class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
43 public:
44   ExtractAPIVisitor(ASTContext &Context, Language Lang)
45       : Context(Context), API(Context.getTargetInfo().getTriple(), Lang) {}
46 
47   const APISet &getAPI() const { return API; }
48 
49   bool VisitVarDecl(const VarDecl *Decl) {
50     // Skip function parameters.
51     if (isa<ParmVarDecl>(Decl))
52       return true;
53 
54     // Skip non-global variables in records (struct/union/class).
55     if (Decl->getDeclContext()->isRecord())
56       return true;
57 
58     // Skip local variables inside function or method.
59     if (!Decl->isDefinedOutsideFunctionOrMethod())
60       return true;
61 
62     // If this is a template but not specialization or instantiation, skip.
63     if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
64         Decl->getTemplateSpecializationKind() == TSK_Undeclared)
65       return true;
66 
67     // Collect symbol information.
68     StringRef Name = Decl->getName();
69     StringRef USR = API.recordUSR(Decl);
70     PresumedLoc Loc =
71         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
72     AvailabilityInfo Availability = getAvailability(Decl);
73     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
74     DocComment Comment;
75     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
76       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
77                                               Context.getDiagnostics());
78 
79     // Build declaration fragments and sub-heading for the variable.
80     DeclarationFragments Declaration =
81         DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
82     DeclarationFragments SubHeading =
83         DeclarationFragmentsBuilder::getSubHeading(Decl);
84 
85     // Add the global variable record to the API set.
86     API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment,
87                      Declaration, SubHeading);
88     return true;
89   }
90 
91   bool VisitFunctionDecl(const FunctionDecl *Decl) {
92     if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
93       // Skip member function in class templates.
94       if (Method->getParent()->getDescribedClassTemplate() != nullptr)
95         return true;
96 
97       // Skip methods in records.
98       for (auto P : Context.getParents(*Method)) {
99         if (P.get<CXXRecordDecl>())
100           return true;
101       }
102 
103       // Skip ConstructorDecl and DestructorDecl.
104       if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
105         return true;
106     }
107 
108     // Skip templated functions.
109     switch (Decl->getTemplatedKind()) {
110     case FunctionDecl::TK_NonTemplate:
111       break;
112     case FunctionDecl::TK_MemberSpecialization:
113     case FunctionDecl::TK_FunctionTemplateSpecialization:
114       if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
115         if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
116           return true;
117       }
118       break;
119     case FunctionDecl::TK_FunctionTemplate:
120     case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
121       return true;
122     }
123 
124     // Collect symbol information.
125     StringRef Name = Decl->getName();
126     StringRef USR = API.recordUSR(Decl);
127     PresumedLoc Loc =
128         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
129     AvailabilityInfo Availability = getAvailability(Decl);
130     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
131     DocComment Comment;
132     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
133       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
134                                               Context.getDiagnostics());
135 
136     // Build declaration fragments, sub-heading, and signature of the function.
137     DeclarationFragments Declaration =
138         DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
139     DeclarationFragments SubHeading =
140         DeclarationFragmentsBuilder::getSubHeading(Decl);
141     FunctionSignature Signature =
142         DeclarationFragmentsBuilder::getFunctionSignature(Decl);
143 
144     // Add the function record to the API set.
145     API.addFunction(Name, USR, Loc, Availability, Linkage, Comment, Declaration,
146                     SubHeading, Signature);
147     return true;
148   }
149 
150   bool VisitEnumDecl(const EnumDecl *Decl) {
151     if (!Decl->isComplete())
152       return true;
153 
154     // Skip forward declaration.
155     if (!Decl->isThisDeclarationADefinition())
156       return true;
157 
158     // Collect symbol information.
159     StringRef Name = Decl->getName();
160     StringRef USR = API.recordUSR(Decl);
161     PresumedLoc Loc =
162         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
163     AvailabilityInfo Availability = getAvailability(Decl);
164     DocComment Comment;
165     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
166       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
167                                               Context.getDiagnostics());
168 
169     // Build declaration fragments and sub-heading for the enum.
170     DeclarationFragments Declaration =
171         DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
172     DeclarationFragments SubHeading =
173         DeclarationFragmentsBuilder::getSubHeading(Decl);
174 
175     EnumRecord *EnumRecord = API.addEnum(Name, USR, Loc, Availability, Comment,
176                                          Declaration, SubHeading);
177 
178     // Now collect information about the enumerators in this enum.
179     recordEnumConstants(EnumRecord, Decl->enumerators());
180 
181     return true;
182   }
183 
184   bool VisitRecordDecl(const RecordDecl *Decl) {
185     if (!Decl->isCompleteDefinition())
186       return true;
187 
188     // Skip C++ structs/classes/unions
189     // TODO: support C++ records
190     if (isa<CXXRecordDecl>(Decl))
191       return true;
192 
193     // Collect symbol information.
194     StringRef Name = Decl->getName();
195     StringRef USR = API.recordUSR(Decl);
196     PresumedLoc Loc =
197         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
198     AvailabilityInfo Availability = getAvailability(Decl);
199     DocComment Comment;
200     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
201       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
202                                               Context.getDiagnostics());
203 
204     // Build declaration fragments and sub-heading for the struct.
205     DeclarationFragments Declaration =
206         DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
207     DeclarationFragments SubHeading =
208         DeclarationFragmentsBuilder::getSubHeading(Decl);
209 
210     StructRecord *StructRecord = API.addStruct(
211         Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
212 
213     // Now collect information about the fields in this struct.
214     recordStructFields(StructRecord, Decl->fields());
215 
216     return true;
217   }
218 
219   bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
220     // Skip forward declaration for classes (@class)
221     if (!Decl->isThisDeclarationADefinition())
222       return true;
223 
224     // Collect symbol information.
225     StringRef Name = Decl->getName();
226     StringRef USR = API.recordUSR(Decl);
227     PresumedLoc Loc =
228         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
229     AvailabilityInfo Availability = getAvailability(Decl);
230     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
231     DocComment Comment;
232     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
233       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
234                                               Context.getDiagnostics());
235 
236     // Build declaration fragments and sub-heading for the interface.
237     DeclarationFragments Declaration =
238         DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
239     DeclarationFragments SubHeading =
240         DeclarationFragmentsBuilder::getSubHeading(Decl);
241 
242     // Collect super class information.
243     SymbolReference SuperClass;
244     if (const auto *SuperClassDecl = Decl->getSuperClass()) {
245       SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
246       SuperClass.USR = API.recordUSR(SuperClassDecl);
247     }
248 
249     ObjCInterfaceRecord *ObjCInterfaceRecord =
250         API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment,
251                              Declaration, SubHeading, SuperClass);
252 
253     // Record all methods (selectors). This doesn't include automatically
254     // synthesized property methods.
255     recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
256     recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
257     recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
258     recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
259 
260     return true;
261   }
262 
263   bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
264     // Skip forward declaration for protocols (@protocol).
265     if (!Decl->isThisDeclarationADefinition())
266       return true;
267 
268     // Collect symbol information.
269     StringRef Name = Decl->getName();
270     StringRef USR = API.recordUSR(Decl);
271     PresumedLoc Loc =
272         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
273     AvailabilityInfo Availability = getAvailability(Decl);
274     DocComment Comment;
275     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
276       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
277                                               Context.getDiagnostics());
278 
279     // Build declaration fragments and sub-heading for the protocol.
280     DeclarationFragments Declaration =
281         DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
282     DeclarationFragments SubHeading =
283         DeclarationFragmentsBuilder::getSubHeading(Decl);
284 
285     ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
286         Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
287 
288     recordObjCMethods(ObjCProtocolRecord, Decl->methods());
289     recordObjCProperties(ObjCProtocolRecord, Decl->properties());
290     recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
291 
292     return true;
293   }
294 
295 private:
296   /// Get availability information of the declaration \p D.
297   AvailabilityInfo getAvailability(const Decl *D) const {
298     StringRef PlatformName = Context.getTargetInfo().getPlatformName();
299 
300     AvailabilityInfo Availability;
301     // Collect availability attributes from all redeclarations.
302     for (const auto *RD : D->redecls()) {
303       for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
304         if (A->getPlatform()->getName() != PlatformName)
305           continue;
306         Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
307                                         A->getObsoleted(), A->getUnavailable(),
308                                         /* UnconditionallyDeprecated */ false,
309                                         /* UnconditionallyUnavailable */ false);
310         break;
311       }
312 
313       if (const auto *A = RD->getAttr<UnavailableAttr>())
314         if (!A->isImplicit()) {
315           Availability.Unavailable = true;
316           Availability.UnconditionallyUnavailable = true;
317         }
318 
319       if (const auto *A = RD->getAttr<DeprecatedAttr>())
320         if (!A->isImplicit())
321           Availability.UnconditionallyDeprecated = true;
322     }
323 
324     return Availability;
325   }
326 
327   /// Collect API information for the enum constants and associate with the
328   /// parent enum.
329   void recordEnumConstants(EnumRecord *EnumRecord,
330                            const EnumDecl::enumerator_range Constants) {
331     for (const auto *Constant : Constants) {
332       // Collect symbol information.
333       StringRef Name = Constant->getName();
334       StringRef USR = API.recordUSR(Constant);
335       PresumedLoc Loc =
336           Context.getSourceManager().getPresumedLoc(Constant->getLocation());
337       AvailabilityInfo Availability = getAvailability(Constant);
338       DocComment Comment;
339       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
340         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
341                                                 Context.getDiagnostics());
342 
343       // Build declaration fragments and sub-heading for the enum constant.
344       DeclarationFragments Declaration =
345           DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
346       DeclarationFragments SubHeading =
347           DeclarationFragmentsBuilder::getSubHeading(Constant);
348 
349       API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
350                           Declaration, SubHeading);
351     }
352   }
353 
354   /// Collect API information for the struct fields and associate with the
355   /// parent struct.
356   void recordStructFields(StructRecord *StructRecord,
357                           const RecordDecl::field_range Fields) {
358     for (const auto *Field : Fields) {
359       // Collect symbol information.
360       StringRef Name = Field->getName();
361       StringRef USR = API.recordUSR(Field);
362       PresumedLoc Loc =
363           Context.getSourceManager().getPresumedLoc(Field->getLocation());
364       AvailabilityInfo Availability = getAvailability(Field);
365       DocComment Comment;
366       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
367         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
368                                                 Context.getDiagnostics());
369 
370       // Build declaration fragments and sub-heading for the struct field.
371       DeclarationFragments Declaration =
372           DeclarationFragmentsBuilder::getFragmentsForField(Field);
373       DeclarationFragments SubHeading =
374           DeclarationFragmentsBuilder::getSubHeading(Field);
375 
376       API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
377                          Declaration, SubHeading);
378     }
379   }
380 
381   /// Collect API information for the Objective-C methods and associate with the
382   /// parent container.
383   void recordObjCMethods(ObjCContainerRecord *Container,
384                          const ObjCContainerDecl::method_range Methods) {
385     for (const auto *Method : Methods) {
386       // Don't record selectors for properties.
387       if (Method->isPropertyAccessor())
388         continue;
389 
390       StringRef Name = API.copyString(Method->getSelector().getAsString());
391       StringRef USR = API.recordUSR(Method);
392       PresumedLoc Loc =
393           Context.getSourceManager().getPresumedLoc(Method->getLocation());
394       AvailabilityInfo Availability = getAvailability(Method);
395       DocComment Comment;
396       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
397         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
398                                                 Context.getDiagnostics());
399 
400       // Build declaration fragments, sub-heading, and signature for the method.
401       DeclarationFragments Declaration =
402           DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
403       DeclarationFragments SubHeading =
404           DeclarationFragmentsBuilder::getSubHeading(Method);
405       FunctionSignature Signature =
406           DeclarationFragmentsBuilder::getFunctionSignature(Method);
407 
408       API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
409                         Declaration, SubHeading, Signature,
410                         Method->isInstanceMethod());
411     }
412   }
413 
414   void recordObjCProperties(ObjCContainerRecord *Container,
415                             const ObjCContainerDecl::prop_range Properties) {
416     for (const auto *Property : Properties) {
417       StringRef Name = Property->getName();
418       StringRef USR = API.recordUSR(Property);
419       PresumedLoc Loc =
420           Context.getSourceManager().getPresumedLoc(Property->getLocation());
421       AvailabilityInfo Availability = getAvailability(Property);
422       DocComment Comment;
423       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
424         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
425                                                 Context.getDiagnostics());
426 
427       // Build declaration fragments and sub-heading for the property.
428       DeclarationFragments Declaration =
429           DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
430       DeclarationFragments SubHeading =
431           DeclarationFragmentsBuilder::getSubHeading(Property);
432 
433       StringRef GetterName =
434           API.copyString(Property->getGetterName().getAsString());
435       StringRef SetterName =
436           API.copyString(Property->getSetterName().getAsString());
437 
438       // Get the attributes for property.
439       unsigned Attributes = ObjCPropertyRecord::NoAttr;
440       if (Property->getPropertyAttributes() &
441           ObjCPropertyAttribute::kind_readonly)
442         Attributes |= ObjCPropertyRecord::ReadOnly;
443       if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
444         Attributes |= ObjCPropertyRecord::Class;
445 
446       API.addObjCProperty(
447           Container, Name, USR, Loc, Availability, Comment, Declaration,
448           SubHeading,
449           static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
450           GetterName, SetterName, Property->isOptional());
451     }
452   }
453 
454   void recordObjCInstanceVariables(
455       ObjCContainerRecord *Container,
456       const llvm::iterator_range<
457           DeclContext::specific_decl_iterator<ObjCIvarDecl>>
458           Ivars) {
459     for (const auto *Ivar : Ivars) {
460       StringRef Name = Ivar->getName();
461       StringRef USR = API.recordUSR(Ivar);
462       PresumedLoc Loc =
463           Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
464       AvailabilityInfo Availability = getAvailability(Ivar);
465       DocComment Comment;
466       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
467         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
468                                                 Context.getDiagnostics());
469 
470       // Build declaration fragments and sub-heading for the instance variable.
471       DeclarationFragments Declaration =
472           DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
473       DeclarationFragments SubHeading =
474           DeclarationFragmentsBuilder::getSubHeading(Ivar);
475 
476       ObjCInstanceVariableRecord::AccessControl Access =
477           Ivar->getCanonicalAccessControl();
478 
479       API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
480                                   Comment, Declaration, SubHeading, Access);
481     }
482   }
483 
484   void recordObjCProtocols(ObjCContainerRecord *Container,
485                            ObjCInterfaceDecl::protocol_range Protocols) {
486     for (const auto *Protocol : Protocols)
487       Container->Protocols.emplace_back(Protocol->getName(),
488                                         API.recordUSR(Protocol));
489   }
490 
491   ASTContext &Context;
492   APISet API;
493 };
494 
495 class ExtractAPIConsumer : public ASTConsumer {
496 public:
497   ExtractAPIConsumer(ASTContext &Context, StringRef ProductName, Language Lang,
498                      std::unique_ptr<raw_pwrite_stream> OS)
499       : Visitor(Context, Lang), ProductName(ProductName), OS(std::move(OS)) {}
500 
501   void HandleTranslationUnit(ASTContext &Context) override {
502     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
503     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
504 
505     // Setup a SymbolGraphSerializer to write out collected API information in
506     // the Symbol Graph format.
507     // FIXME: Make the kind of APISerializer configurable.
508     SymbolGraphSerializer SGSerializer(Visitor.getAPI(), ProductName);
509     SGSerializer.serialize(*OS);
510   }
511 
512 private:
513   ExtractAPIVisitor Visitor;
514   std::string ProductName;
515   std::unique_ptr<raw_pwrite_stream> OS;
516 };
517 
518 } // namespace
519 
520 std::unique_ptr<ASTConsumer>
521 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
522   std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile);
523   if (!OS)
524     return nullptr;
525   return std::make_unique<ExtractAPIConsumer>(
526       CI.getASTContext(), CI.getInvocation().getFrontendOpts().ProductName,
527       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(),
528       std::move(OS));
529 }
530 
531 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
532   auto &Inputs = CI.getFrontendOpts().Inputs;
533   if (Inputs.empty())
534     return true;
535 
536   auto Kind = Inputs[0].getKind();
537 
538   // Convert the header file inputs into a single input buffer.
539   SmallString<256> HeaderContents;
540   for (const FrontendInputFile &FIF : Inputs) {
541     if (Kind.isObjectiveC())
542       HeaderContents += "#import";
543     else
544       HeaderContents += "#include";
545     HeaderContents += " \"";
546     HeaderContents += FIF.getFile();
547     HeaderContents += "\"\n";
548   }
549 
550   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
551                                                 getInputBufferName());
552 
553   // Set that buffer up as our "real" input in the CompilerInstance.
554   Inputs.clear();
555   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
556 
557   return true;
558 }
559 
560 std::unique_ptr<raw_pwrite_stream>
561 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
562   std::unique_ptr<raw_pwrite_stream> OS =
563       CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
564                                  /*RemoveFileOnSignal=*/false);
565   if (!OS)
566     return nullptr;
567   return OS;
568 }
569