1*89f6b26fSZixu Wang //===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===//
2*89f6b26fSZixu Wang //
3*89f6b26fSZixu Wang // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*89f6b26fSZixu Wang // See https://llvm.org/LICENSE.txt for license information.
5*89f6b26fSZixu Wang // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*89f6b26fSZixu Wang //
7*89f6b26fSZixu Wang //===----------------------------------------------------------------------===//
8*89f6b26fSZixu Wang ///
9*89f6b26fSZixu Wang /// \file
10*89f6b26fSZixu Wang /// This file implements the ExtractAPIAction, and ASTVisitor/Consumer to
11*89f6b26fSZixu Wang /// collect API information.
12*89f6b26fSZixu Wang ///
13*89f6b26fSZixu Wang //===----------------------------------------------------------------------===//
14*89f6b26fSZixu Wang 
15*89f6b26fSZixu Wang #include "clang/AST/ASTConsumer.h"
16*89f6b26fSZixu Wang #include "clang/AST/ASTContext.h"
17*89f6b26fSZixu Wang #include "clang/AST/Decl.h"
18*89f6b26fSZixu Wang #include "clang/AST/DeclCXX.h"
19*89f6b26fSZixu Wang #include "clang/AST/ParentMapContext.h"
20*89f6b26fSZixu Wang #include "clang/AST/RawCommentList.h"
21*89f6b26fSZixu Wang #include "clang/AST/RecursiveASTVisitor.h"
22*89f6b26fSZixu Wang #include "clang/Basic/TargetInfo.h"
23*89f6b26fSZixu Wang #include "clang/ExtractAPI/API.h"
24*89f6b26fSZixu Wang #include "clang/ExtractAPI/AvailabilityInfo.h"
25*89f6b26fSZixu Wang #include "clang/ExtractAPI/DeclarationFragments.h"
26*89f6b26fSZixu Wang #include "clang/ExtractAPI/FrontendActions.h"
27*89f6b26fSZixu Wang #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
28*89f6b26fSZixu Wang #include "clang/Frontend/ASTConsumers.h"
29*89f6b26fSZixu Wang #include "clang/Frontend/CompilerInstance.h"
30*89f6b26fSZixu Wang #include "llvm/Support/raw_ostream.h"
31*89f6b26fSZixu Wang 
32*89f6b26fSZixu Wang using namespace clang;
33*89f6b26fSZixu Wang using namespace extractapi;
34*89f6b26fSZixu Wang 
35*89f6b26fSZixu Wang namespace {
36*89f6b26fSZixu Wang 
37*89f6b26fSZixu Wang /// The RecursiveASTVisitor to traverse symbol declarations and collect API
38*89f6b26fSZixu Wang /// information.
39*89f6b26fSZixu Wang class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
40*89f6b26fSZixu Wang public:
41*89f6b26fSZixu Wang   explicit ExtractAPIVisitor(ASTContext &Context)
42*89f6b26fSZixu Wang       : Context(Context),
43*89f6b26fSZixu Wang         API(Context.getTargetInfo().getTriple(), Context.getLangOpts()) {}
44*89f6b26fSZixu Wang 
45*89f6b26fSZixu Wang   const APISet &getAPI() const { return API; }
46*89f6b26fSZixu Wang 
47*89f6b26fSZixu Wang   bool VisitVarDecl(const VarDecl *Decl) {
48*89f6b26fSZixu Wang     // Skip function parameters.
49*89f6b26fSZixu Wang     if (isa<ParmVarDecl>(Decl))
50*89f6b26fSZixu Wang       return true;
51*89f6b26fSZixu Wang 
52*89f6b26fSZixu Wang     // Skip non-global variables in records (struct/union/class).
53*89f6b26fSZixu Wang     if (Decl->getDeclContext()->isRecord())
54*89f6b26fSZixu Wang       return true;
55*89f6b26fSZixu Wang 
56*89f6b26fSZixu Wang     // Skip local variables inside function or method.
57*89f6b26fSZixu Wang     if (!Decl->isDefinedOutsideFunctionOrMethod())
58*89f6b26fSZixu Wang       return true;
59*89f6b26fSZixu Wang 
60*89f6b26fSZixu Wang     // If this is a template but not specialization or instantiation, skip.
61*89f6b26fSZixu Wang     if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
62*89f6b26fSZixu Wang         Decl->getTemplateSpecializationKind() == TSK_Undeclared)
63*89f6b26fSZixu Wang       return true;
64*89f6b26fSZixu Wang 
65*89f6b26fSZixu Wang     // Collect symbol information.
66*89f6b26fSZixu Wang     StringRef Name = Decl->getName();
67*89f6b26fSZixu Wang     StringRef USR = API.recordUSR(Decl);
68*89f6b26fSZixu Wang     PresumedLoc Loc =
69*89f6b26fSZixu Wang         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
70*89f6b26fSZixu Wang     AvailabilityInfo Availability = getAvailability(Decl);
71*89f6b26fSZixu Wang     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
72*89f6b26fSZixu Wang     DocComment Comment;
73*89f6b26fSZixu Wang     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
74*89f6b26fSZixu Wang       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
75*89f6b26fSZixu Wang                                               Context.getDiagnostics());
76*89f6b26fSZixu Wang 
77*89f6b26fSZixu Wang     // Build declaration fragments and sub-heading for the variable.
78*89f6b26fSZixu Wang     DeclarationFragments Declaration =
79*89f6b26fSZixu Wang         DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
80*89f6b26fSZixu Wang     DeclarationFragments SubHeading =
81*89f6b26fSZixu Wang         DeclarationFragmentsBuilder::getSubHeading(Decl);
82*89f6b26fSZixu Wang 
83*89f6b26fSZixu Wang     // Add the global variable record to the API set.
84*89f6b26fSZixu Wang     API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment,
85*89f6b26fSZixu Wang                      Declaration, SubHeading);
86*89f6b26fSZixu Wang     return true;
87*89f6b26fSZixu Wang   }
88*89f6b26fSZixu Wang 
89*89f6b26fSZixu Wang   bool VisitFunctionDecl(const FunctionDecl *Decl) {
90*89f6b26fSZixu Wang     if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
91*89f6b26fSZixu Wang       // Skip member function in class templates.
92*89f6b26fSZixu Wang       if (Method->getParent()->getDescribedClassTemplate() != nullptr)
93*89f6b26fSZixu Wang         return true;
94*89f6b26fSZixu Wang 
95*89f6b26fSZixu Wang       // Skip methods in records.
96*89f6b26fSZixu Wang       for (auto P : Context.getParents(*Method)) {
97*89f6b26fSZixu Wang         if (P.get<CXXRecordDecl>())
98*89f6b26fSZixu Wang           return true;
99*89f6b26fSZixu Wang       }
100*89f6b26fSZixu Wang 
101*89f6b26fSZixu Wang       // Skip ConstructorDecl and DestructorDecl.
102*89f6b26fSZixu Wang       if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
103*89f6b26fSZixu Wang         return true;
104*89f6b26fSZixu Wang     }
105*89f6b26fSZixu Wang 
106*89f6b26fSZixu Wang     // Skip templated functions.
107*89f6b26fSZixu Wang     switch (Decl->getTemplatedKind()) {
108*89f6b26fSZixu Wang     case FunctionDecl::TK_NonTemplate:
109*89f6b26fSZixu Wang       break;
110*89f6b26fSZixu Wang     case FunctionDecl::TK_MemberSpecialization:
111*89f6b26fSZixu Wang     case FunctionDecl::TK_FunctionTemplateSpecialization:
112*89f6b26fSZixu Wang       if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
113*89f6b26fSZixu Wang         if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
114*89f6b26fSZixu Wang           return true;
115*89f6b26fSZixu Wang       }
116*89f6b26fSZixu Wang       break;
117*89f6b26fSZixu Wang     case FunctionDecl::TK_FunctionTemplate:
118*89f6b26fSZixu Wang     case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
119*89f6b26fSZixu Wang       return true;
120*89f6b26fSZixu Wang     }
121*89f6b26fSZixu Wang 
122*89f6b26fSZixu Wang     // Collect symbol information.
123*89f6b26fSZixu Wang     StringRef Name = Decl->getName();
124*89f6b26fSZixu Wang     StringRef USR = API.recordUSR(Decl);
125*89f6b26fSZixu Wang     PresumedLoc Loc =
126*89f6b26fSZixu Wang         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
127*89f6b26fSZixu Wang     AvailabilityInfo Availability = getAvailability(Decl);
128*89f6b26fSZixu Wang     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
129*89f6b26fSZixu Wang     DocComment Comment;
130*89f6b26fSZixu Wang     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
131*89f6b26fSZixu Wang       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
132*89f6b26fSZixu Wang                                               Context.getDiagnostics());
133*89f6b26fSZixu Wang 
134*89f6b26fSZixu Wang     // Build declaration fragments, sub-heading, and signature of the function.
135*89f6b26fSZixu Wang     DeclarationFragments Declaration =
136*89f6b26fSZixu Wang         DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
137*89f6b26fSZixu Wang     DeclarationFragments SubHeading =
138*89f6b26fSZixu Wang         DeclarationFragmentsBuilder::getSubHeading(Decl);
139*89f6b26fSZixu Wang     FunctionSignature Signature =
140*89f6b26fSZixu Wang         DeclarationFragmentsBuilder::getFunctionSignature(Decl);
141*89f6b26fSZixu Wang 
142*89f6b26fSZixu Wang     // Add the function record to the API set.
143*89f6b26fSZixu Wang     API.addFunction(Name, USR, Loc, Availability, Linkage, Comment, Declaration,
144*89f6b26fSZixu Wang                     SubHeading, Signature);
145*89f6b26fSZixu Wang     return true;
146*89f6b26fSZixu Wang   }
147*89f6b26fSZixu Wang 
148*89f6b26fSZixu Wang private:
149*89f6b26fSZixu Wang   /// Get availability information of the declaration \p D.
150*89f6b26fSZixu Wang   AvailabilityInfo getAvailability(const Decl *D) const {
151*89f6b26fSZixu Wang     StringRef PlatformName = Context.getTargetInfo().getPlatformName();
152*89f6b26fSZixu Wang 
153*89f6b26fSZixu Wang     AvailabilityInfo Availability;
154*89f6b26fSZixu Wang     // Collect availability attributes from all redeclarations.
155*89f6b26fSZixu Wang     for (const auto *RD : D->redecls()) {
156*89f6b26fSZixu Wang       for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
157*89f6b26fSZixu Wang         if (A->getPlatform()->getName() != PlatformName)
158*89f6b26fSZixu Wang           continue;
159*89f6b26fSZixu Wang         Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
160*89f6b26fSZixu Wang                                         A->getObsoleted(), A->getUnavailable(),
161*89f6b26fSZixu Wang                                         /* UnconditionallyDeprecated */ false,
162*89f6b26fSZixu Wang                                         /* UnconditionallyUnavailable */ false);
163*89f6b26fSZixu Wang         break;
164*89f6b26fSZixu Wang       }
165*89f6b26fSZixu Wang 
166*89f6b26fSZixu Wang       if (const auto *A = RD->getAttr<UnavailableAttr>())
167*89f6b26fSZixu Wang         if (!A->isImplicit()) {
168*89f6b26fSZixu Wang           Availability.Unavailable = true;
169*89f6b26fSZixu Wang           Availability.UnconditionallyUnavailable = true;
170*89f6b26fSZixu Wang         }
171*89f6b26fSZixu Wang 
172*89f6b26fSZixu Wang       if (const auto *A = RD->getAttr<DeprecatedAttr>())
173*89f6b26fSZixu Wang         if (!A->isImplicit())
174*89f6b26fSZixu Wang           Availability.UnconditionallyDeprecated = true;
175*89f6b26fSZixu Wang     }
176*89f6b26fSZixu Wang 
177*89f6b26fSZixu Wang     return Availability;
178*89f6b26fSZixu Wang   }
179*89f6b26fSZixu Wang 
180*89f6b26fSZixu Wang   ASTContext &Context;
181*89f6b26fSZixu Wang   APISet API;
182*89f6b26fSZixu Wang };
183*89f6b26fSZixu Wang 
184*89f6b26fSZixu Wang class ExtractAPIConsumer : public ASTConsumer {
185*89f6b26fSZixu Wang public:
186*89f6b26fSZixu Wang   ExtractAPIConsumer(ASTContext &Context, std::unique_ptr<raw_pwrite_stream> OS)
187*89f6b26fSZixu Wang       : Visitor(Context), OS(std::move(OS)) {}
188*89f6b26fSZixu Wang 
189*89f6b26fSZixu Wang   void HandleTranslationUnit(ASTContext &Context) override {
190*89f6b26fSZixu Wang     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
191*89f6b26fSZixu Wang     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
192*89f6b26fSZixu Wang 
193*89f6b26fSZixu Wang     // Setup a SymbolGraphSerializer to write out collected API information in
194*89f6b26fSZixu Wang     // the Symbol Graph format.
195*89f6b26fSZixu Wang     // FIXME: Make the kind of APISerializer configurable.
196*89f6b26fSZixu Wang     SymbolGraphSerializer SGSerializer(Visitor.getAPI());
197*89f6b26fSZixu Wang     SGSerializer.serialize(*OS);
198*89f6b26fSZixu Wang   }
199*89f6b26fSZixu Wang 
200*89f6b26fSZixu Wang private:
201*89f6b26fSZixu Wang   ExtractAPIVisitor Visitor;
202*89f6b26fSZixu Wang   std::unique_ptr<raw_pwrite_stream> OS;
203*89f6b26fSZixu Wang };
204*89f6b26fSZixu Wang 
205*89f6b26fSZixu Wang } // namespace
206*89f6b26fSZixu Wang 
207*89f6b26fSZixu Wang std::unique_ptr<ASTConsumer>
208*89f6b26fSZixu Wang ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
209*89f6b26fSZixu Wang   std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile);
210*89f6b26fSZixu Wang   if (!OS)
211*89f6b26fSZixu Wang     return nullptr;
212*89f6b26fSZixu Wang   return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
213*89f6b26fSZixu Wang                                               std::move(OS));
214*89f6b26fSZixu Wang }
215*89f6b26fSZixu Wang 
216*89f6b26fSZixu Wang std::unique_ptr<raw_pwrite_stream>
217*89f6b26fSZixu Wang ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
218*89f6b26fSZixu Wang   std::unique_ptr<raw_pwrite_stream> OS =
219*89f6b26fSZixu Wang       CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
220*89f6b26fSZixu Wang                                  /*RemoveFileOnSignal=*/false);
221*89f6b26fSZixu Wang   if (!OS)
222*89f6b26fSZixu Wang     return nullptr;
223*89f6b26fSZixu Wang   return OS;
224*89f6b26fSZixu Wang }
225