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