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 private:
264   /// Get availability information of the declaration \p D.
265   AvailabilityInfo getAvailability(const Decl *D) const {
266     StringRef PlatformName = Context.getTargetInfo().getPlatformName();
267 
268     AvailabilityInfo Availability;
269     // Collect availability attributes from all redeclarations.
270     for (const auto *RD : D->redecls()) {
271       for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
272         if (A->getPlatform()->getName() != PlatformName)
273           continue;
274         Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
275                                         A->getObsoleted(), A->getUnavailable(),
276                                         /* UnconditionallyDeprecated */ false,
277                                         /* UnconditionallyUnavailable */ false);
278         break;
279       }
280 
281       if (const auto *A = RD->getAttr<UnavailableAttr>())
282         if (!A->isImplicit()) {
283           Availability.Unavailable = true;
284           Availability.UnconditionallyUnavailable = true;
285         }
286 
287       if (const auto *A = RD->getAttr<DeprecatedAttr>())
288         if (!A->isImplicit())
289           Availability.UnconditionallyDeprecated = true;
290     }
291 
292     return Availability;
293   }
294 
295   /// Collect API information for the enum constants and associate with the
296   /// parent enum.
297   void recordEnumConstants(EnumRecord *EnumRecord,
298                            const EnumDecl::enumerator_range Constants) {
299     for (const auto *Constant : Constants) {
300       // Collect symbol information.
301       StringRef Name = Constant->getName();
302       StringRef USR = API.recordUSR(Constant);
303       PresumedLoc Loc =
304           Context.getSourceManager().getPresumedLoc(Constant->getLocation());
305       AvailabilityInfo Availability = getAvailability(Constant);
306       DocComment Comment;
307       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
308         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
309                                                 Context.getDiagnostics());
310 
311       // Build declaration fragments and sub-heading for the enum constant.
312       DeclarationFragments Declaration =
313           DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
314       DeclarationFragments SubHeading =
315           DeclarationFragmentsBuilder::getSubHeading(Constant);
316 
317       API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
318                           Declaration, SubHeading);
319     }
320   }
321 
322   /// Collect API information for the struct fields and associate with the
323   /// parent struct.
324   void recordStructFields(StructRecord *StructRecord,
325                           const RecordDecl::field_range Fields) {
326     for (const auto *Field : Fields) {
327       // Collect symbol information.
328       StringRef Name = Field->getName();
329       StringRef USR = API.recordUSR(Field);
330       PresumedLoc Loc =
331           Context.getSourceManager().getPresumedLoc(Field->getLocation());
332       AvailabilityInfo Availability = getAvailability(Field);
333       DocComment Comment;
334       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
335         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
336                                                 Context.getDiagnostics());
337 
338       // Build declaration fragments and sub-heading for the struct field.
339       DeclarationFragments Declaration =
340           DeclarationFragmentsBuilder::getFragmentsForField(Field);
341       DeclarationFragments SubHeading =
342           DeclarationFragmentsBuilder::getSubHeading(Field);
343 
344       API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
345                          Declaration, SubHeading);
346     }
347   }
348 
349   /// Collect API information for the Objective-C methods and associate with the
350   /// parent container.
351   void recordObjCMethods(ObjCContainerRecord *Container,
352                          const ObjCContainerDecl::method_range Methods) {
353     for (const auto *Method : Methods) {
354       // Don't record selectors for properties.
355       if (Method->isPropertyAccessor())
356         continue;
357 
358       StringRef Name = API.copyString(Method->getSelector().getAsString());
359       StringRef USR = API.recordUSR(Method);
360       PresumedLoc Loc =
361           Context.getSourceManager().getPresumedLoc(Method->getLocation());
362       AvailabilityInfo Availability = getAvailability(Method);
363       DocComment Comment;
364       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
365         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
366                                                 Context.getDiagnostics());
367 
368       // Build declaration fragments, sub-heading, and signature for the method.
369       DeclarationFragments Declaration =
370           DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
371       DeclarationFragments SubHeading =
372           DeclarationFragmentsBuilder::getSubHeading(Method);
373       FunctionSignature Signature =
374           DeclarationFragmentsBuilder::getFunctionSignature(Method);
375 
376       API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
377                         Declaration, SubHeading, Signature,
378                         Method->isInstanceMethod());
379     }
380   }
381 
382   void recordObjCProperties(ObjCContainerRecord *Container,
383                             const ObjCContainerDecl::prop_range Properties) {
384     for (const auto *Property : Properties) {
385       StringRef Name = Property->getName();
386       StringRef USR = API.recordUSR(Property);
387       PresumedLoc Loc =
388           Context.getSourceManager().getPresumedLoc(Property->getLocation());
389       AvailabilityInfo Availability = getAvailability(Property);
390       DocComment Comment;
391       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
392         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
393                                                 Context.getDiagnostics());
394 
395       // Build declaration fragments and sub-heading for the property.
396       DeclarationFragments Declaration =
397           DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
398       DeclarationFragments SubHeading =
399           DeclarationFragmentsBuilder::getSubHeading(Property);
400 
401       StringRef GetterName =
402           API.copyString(Property->getGetterName().getAsString());
403       StringRef SetterName =
404           API.copyString(Property->getSetterName().getAsString());
405 
406       // Get the attributes for property.
407       unsigned Attributes = ObjCPropertyRecord::NoAttr;
408       if (Property->getPropertyAttributes() &
409           ObjCPropertyAttribute::kind_readonly)
410         Attributes |= ObjCPropertyRecord::ReadOnly;
411       if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
412         Attributes |= ObjCPropertyRecord::Class;
413 
414       API.addObjCProperty(
415           Container, Name, USR, Loc, Availability, Comment, Declaration,
416           SubHeading,
417           static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
418           GetterName, SetterName, Property->isOptional());
419     }
420   }
421 
422   void recordObjCInstanceVariables(
423       ObjCContainerRecord *Container,
424       const llvm::iterator_range<
425           DeclContext::specific_decl_iterator<ObjCIvarDecl>>
426           Ivars) {
427     for (const auto *Ivar : Ivars) {
428       StringRef Name = Ivar->getName();
429       StringRef USR = API.recordUSR(Ivar);
430       PresumedLoc Loc =
431           Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
432       AvailabilityInfo Availability = getAvailability(Ivar);
433       DocComment Comment;
434       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
435         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
436                                                 Context.getDiagnostics());
437 
438       // Build declaration fragments and sub-heading for the instance variable.
439       DeclarationFragments Declaration =
440           DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
441       DeclarationFragments SubHeading =
442           DeclarationFragmentsBuilder::getSubHeading(Ivar);
443 
444       ObjCInstanceVariableRecord::AccessControl Access =
445           Ivar->getCanonicalAccessControl();
446 
447       API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
448                                   Comment, Declaration, SubHeading, Access);
449     }
450   }
451 
452   void recordObjCProtocols(ObjCContainerRecord *Container,
453                            ObjCInterfaceDecl::protocol_range Protocols) {
454     for (const auto *Protocol : Protocols)
455       Container->Protocols.emplace_back(Protocol->getName(),
456                                         API.recordUSR(Protocol));
457   }
458 
459   ASTContext &Context;
460   APISet API;
461 };
462 
463 class ExtractAPIConsumer : public ASTConsumer {
464 public:
465   ExtractAPIConsumer(ASTContext &Context, StringRef ProductName, Language Lang,
466                      std::unique_ptr<raw_pwrite_stream> OS)
467       : Visitor(Context, Lang), ProductName(ProductName), OS(std::move(OS)) {}
468 
469   void HandleTranslationUnit(ASTContext &Context) override {
470     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
471     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
472 
473     // Setup a SymbolGraphSerializer to write out collected API information in
474     // the Symbol Graph format.
475     // FIXME: Make the kind of APISerializer configurable.
476     SymbolGraphSerializer SGSerializer(Visitor.getAPI(), ProductName);
477     SGSerializer.serialize(*OS);
478   }
479 
480 private:
481   ExtractAPIVisitor Visitor;
482   std::string ProductName;
483   std::unique_ptr<raw_pwrite_stream> OS;
484 };
485 
486 } // namespace
487 
488 std::unique_ptr<ASTConsumer>
489 ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
490   std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile);
491   if (!OS)
492     return nullptr;
493   return std::make_unique<ExtractAPIConsumer>(
494       CI.getASTContext(), CI.getInvocation().getFrontendOpts().ProductName,
495       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(),
496       std::move(OS));
497 }
498 
499 bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
500   auto &Inputs = CI.getFrontendOpts().Inputs;
501   if (Inputs.empty())
502     return true;
503 
504   auto Kind = Inputs[0].getKind();
505 
506   // Convert the header file inputs into a single input buffer.
507   SmallString<256> HeaderContents;
508   for (const FrontendInputFile &FIF : Inputs) {
509     if (Kind.isObjectiveC())
510       HeaderContents += "#import";
511     else
512       HeaderContents += "#include";
513     HeaderContents += " \"";
514     HeaderContents += FIF.getFile();
515     HeaderContents += "\"\n";
516   }
517 
518   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
519                                                 getInputBufferName());
520 
521   // Set that buffer up as our "real" input in the CompilerInstance.
522   Inputs.clear();
523   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
524 
525   return true;
526 }
527 
528 std::unique_ptr<raw_pwrite_stream>
529 ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
530   std::unique_ptr<raw_pwrite_stream> OS =
531       CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
532                                  /*RemoveFileOnSignal=*/false);
533   if (!OS)
534     return nullptr;
535   return OS;
536 }
537