189f6b26fSZixu Wang //===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===//
289f6b26fSZixu Wang //
389f6b26fSZixu Wang // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
489f6b26fSZixu Wang // See https://llvm.org/LICENSE.txt for license information.
589f6b26fSZixu Wang // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
689f6b26fSZixu Wang //
789f6b26fSZixu Wang //===----------------------------------------------------------------------===//
889f6b26fSZixu Wang ///
989f6b26fSZixu Wang /// \file
1089f6b26fSZixu Wang /// This file implements the ExtractAPIAction, and ASTVisitor/Consumer to
1189f6b26fSZixu Wang /// collect API information.
1289f6b26fSZixu Wang ///
1389f6b26fSZixu Wang //===----------------------------------------------------------------------===//
1489f6b26fSZixu Wang 
1589f6b26fSZixu Wang #include "clang/AST/ASTConsumer.h"
1689f6b26fSZixu Wang #include "clang/AST/ASTContext.h"
1789f6b26fSZixu Wang #include "clang/AST/Decl.h"
1889f6b26fSZixu Wang #include "clang/AST/DeclCXX.h"
1989f6b26fSZixu Wang #include "clang/AST/ParentMapContext.h"
2089f6b26fSZixu Wang #include "clang/AST/RawCommentList.h"
2189f6b26fSZixu Wang #include "clang/AST/RecursiveASTVisitor.h"
2289f6b26fSZixu Wang #include "clang/Basic/TargetInfo.h"
2389f6b26fSZixu Wang #include "clang/ExtractAPI/API.h"
2489f6b26fSZixu Wang #include "clang/ExtractAPI/AvailabilityInfo.h"
2589f6b26fSZixu Wang #include "clang/ExtractAPI/DeclarationFragments.h"
2689f6b26fSZixu Wang #include "clang/ExtractAPI/FrontendActions.h"
2789f6b26fSZixu Wang #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
2889f6b26fSZixu Wang #include "clang/Frontend/ASTConsumers.h"
2989f6b26fSZixu Wang #include "clang/Frontend/CompilerInstance.h"
3089f6b26fSZixu Wang #include "llvm/Support/raw_ostream.h"
3189f6b26fSZixu Wang 
3289f6b26fSZixu Wang using namespace clang;
3389f6b26fSZixu Wang using namespace extractapi;
3489f6b26fSZixu Wang 
3589f6b26fSZixu Wang namespace {
3689f6b26fSZixu Wang 
3789f6b26fSZixu Wang /// The RecursiveASTVisitor to traverse symbol declarations and collect API
3889f6b26fSZixu Wang /// information.
3989f6b26fSZixu Wang class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
4089f6b26fSZixu Wang public:
4189f6b26fSZixu Wang   explicit ExtractAPIVisitor(ASTContext &Context)
4289f6b26fSZixu Wang       : Context(Context),
4389f6b26fSZixu Wang         API(Context.getTargetInfo().getTriple(), Context.getLangOpts()) {}
4489f6b26fSZixu Wang 
4589f6b26fSZixu Wang   const APISet &getAPI() const { return API; }
4689f6b26fSZixu Wang 
4789f6b26fSZixu Wang   bool VisitVarDecl(const VarDecl *Decl) {
4889f6b26fSZixu Wang     // Skip function parameters.
4989f6b26fSZixu Wang     if (isa<ParmVarDecl>(Decl))
5089f6b26fSZixu Wang       return true;
5189f6b26fSZixu Wang 
5289f6b26fSZixu Wang     // Skip non-global variables in records (struct/union/class).
5389f6b26fSZixu Wang     if (Decl->getDeclContext()->isRecord())
5489f6b26fSZixu Wang       return true;
5589f6b26fSZixu Wang 
5689f6b26fSZixu Wang     // Skip local variables inside function or method.
5789f6b26fSZixu Wang     if (!Decl->isDefinedOutsideFunctionOrMethod())
5889f6b26fSZixu Wang       return true;
5989f6b26fSZixu Wang 
6089f6b26fSZixu Wang     // If this is a template but not specialization or instantiation, skip.
6189f6b26fSZixu Wang     if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
6289f6b26fSZixu Wang         Decl->getTemplateSpecializationKind() == TSK_Undeclared)
6389f6b26fSZixu Wang       return true;
6489f6b26fSZixu Wang 
6589f6b26fSZixu Wang     // Collect symbol information.
6689f6b26fSZixu Wang     StringRef Name = Decl->getName();
6789f6b26fSZixu Wang     StringRef USR = API.recordUSR(Decl);
6889f6b26fSZixu Wang     PresumedLoc Loc =
6989f6b26fSZixu Wang         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
7089f6b26fSZixu Wang     AvailabilityInfo Availability = getAvailability(Decl);
7189f6b26fSZixu Wang     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
7289f6b26fSZixu Wang     DocComment Comment;
7389f6b26fSZixu Wang     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
7489f6b26fSZixu Wang       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
7589f6b26fSZixu Wang                                               Context.getDiagnostics());
7689f6b26fSZixu Wang 
7789f6b26fSZixu Wang     // Build declaration fragments and sub-heading for the variable.
7889f6b26fSZixu Wang     DeclarationFragments Declaration =
7989f6b26fSZixu Wang         DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
8089f6b26fSZixu Wang     DeclarationFragments SubHeading =
8189f6b26fSZixu Wang         DeclarationFragmentsBuilder::getSubHeading(Decl);
8289f6b26fSZixu Wang 
8389f6b26fSZixu Wang     // Add the global variable record to the API set.
8489f6b26fSZixu Wang     API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment,
8589f6b26fSZixu Wang                      Declaration, SubHeading);
8689f6b26fSZixu Wang     return true;
8789f6b26fSZixu Wang   }
8889f6b26fSZixu Wang 
8989f6b26fSZixu Wang   bool VisitFunctionDecl(const FunctionDecl *Decl) {
9089f6b26fSZixu Wang     if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
9189f6b26fSZixu Wang       // Skip member function in class templates.
9289f6b26fSZixu Wang       if (Method->getParent()->getDescribedClassTemplate() != nullptr)
9389f6b26fSZixu Wang         return true;
9489f6b26fSZixu Wang 
9589f6b26fSZixu Wang       // Skip methods in records.
9689f6b26fSZixu Wang       for (auto P : Context.getParents(*Method)) {
9789f6b26fSZixu Wang         if (P.get<CXXRecordDecl>())
9889f6b26fSZixu Wang           return true;
9989f6b26fSZixu Wang       }
10089f6b26fSZixu Wang 
10189f6b26fSZixu Wang       // Skip ConstructorDecl and DestructorDecl.
10289f6b26fSZixu Wang       if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
10389f6b26fSZixu Wang         return true;
10489f6b26fSZixu Wang     }
10589f6b26fSZixu Wang 
10689f6b26fSZixu Wang     // Skip templated functions.
10789f6b26fSZixu Wang     switch (Decl->getTemplatedKind()) {
10889f6b26fSZixu Wang     case FunctionDecl::TK_NonTemplate:
10989f6b26fSZixu Wang       break;
11089f6b26fSZixu Wang     case FunctionDecl::TK_MemberSpecialization:
11189f6b26fSZixu Wang     case FunctionDecl::TK_FunctionTemplateSpecialization:
11289f6b26fSZixu Wang       if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
11389f6b26fSZixu Wang         if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
11489f6b26fSZixu Wang           return true;
11589f6b26fSZixu Wang       }
11689f6b26fSZixu Wang       break;
11789f6b26fSZixu Wang     case FunctionDecl::TK_FunctionTemplate:
11889f6b26fSZixu Wang     case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
11989f6b26fSZixu Wang       return true;
12089f6b26fSZixu Wang     }
12189f6b26fSZixu Wang 
12289f6b26fSZixu Wang     // Collect symbol information.
12389f6b26fSZixu Wang     StringRef Name = Decl->getName();
12489f6b26fSZixu Wang     StringRef USR = API.recordUSR(Decl);
12589f6b26fSZixu Wang     PresumedLoc Loc =
12689f6b26fSZixu Wang         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
12789f6b26fSZixu Wang     AvailabilityInfo Availability = getAvailability(Decl);
12889f6b26fSZixu Wang     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
12989f6b26fSZixu Wang     DocComment Comment;
13089f6b26fSZixu Wang     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
13189f6b26fSZixu Wang       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
13289f6b26fSZixu Wang                                               Context.getDiagnostics());
13389f6b26fSZixu Wang 
13489f6b26fSZixu Wang     // Build declaration fragments, sub-heading, and signature of the function.
13589f6b26fSZixu Wang     DeclarationFragments Declaration =
13689f6b26fSZixu Wang         DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
13789f6b26fSZixu Wang     DeclarationFragments SubHeading =
13889f6b26fSZixu Wang         DeclarationFragmentsBuilder::getSubHeading(Decl);
13989f6b26fSZixu Wang     FunctionSignature Signature =
14089f6b26fSZixu Wang         DeclarationFragmentsBuilder::getFunctionSignature(Decl);
14189f6b26fSZixu Wang 
14289f6b26fSZixu Wang     // Add the function record to the API set.
14389f6b26fSZixu Wang     API.addFunction(Name, USR, Loc, Availability, Linkage, Comment, Declaration,
14489f6b26fSZixu Wang                     SubHeading, Signature);
14589f6b26fSZixu Wang     return true;
14689f6b26fSZixu Wang   }
14789f6b26fSZixu Wang 
14889f6b26fSZixu Wang private:
14989f6b26fSZixu Wang   /// Get availability information of the declaration \p D.
15089f6b26fSZixu Wang   AvailabilityInfo getAvailability(const Decl *D) const {
15189f6b26fSZixu Wang     StringRef PlatformName = Context.getTargetInfo().getPlatformName();
15289f6b26fSZixu Wang 
15389f6b26fSZixu Wang     AvailabilityInfo Availability;
15489f6b26fSZixu Wang     // Collect availability attributes from all redeclarations.
15589f6b26fSZixu Wang     for (const auto *RD : D->redecls()) {
15689f6b26fSZixu Wang       for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
15789f6b26fSZixu Wang         if (A->getPlatform()->getName() != PlatformName)
15889f6b26fSZixu Wang           continue;
15989f6b26fSZixu Wang         Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
16089f6b26fSZixu Wang                                         A->getObsoleted(), A->getUnavailable(),
16189f6b26fSZixu Wang                                         /* UnconditionallyDeprecated */ false,
16289f6b26fSZixu Wang                                         /* UnconditionallyUnavailable */ false);
16389f6b26fSZixu Wang         break;
16489f6b26fSZixu Wang       }
16589f6b26fSZixu Wang 
16689f6b26fSZixu Wang       if (const auto *A = RD->getAttr<UnavailableAttr>())
16789f6b26fSZixu Wang         if (!A->isImplicit()) {
16889f6b26fSZixu Wang           Availability.Unavailable = true;
16989f6b26fSZixu Wang           Availability.UnconditionallyUnavailable = true;
17089f6b26fSZixu Wang         }
17189f6b26fSZixu Wang 
17289f6b26fSZixu Wang       if (const auto *A = RD->getAttr<DeprecatedAttr>())
17389f6b26fSZixu Wang         if (!A->isImplicit())
17489f6b26fSZixu Wang           Availability.UnconditionallyDeprecated = true;
17589f6b26fSZixu Wang     }
17689f6b26fSZixu Wang 
17789f6b26fSZixu Wang     return Availability;
17889f6b26fSZixu Wang   }
17989f6b26fSZixu Wang 
18089f6b26fSZixu Wang   ASTContext &Context;
18189f6b26fSZixu Wang   APISet API;
18289f6b26fSZixu Wang };
18389f6b26fSZixu Wang 
18489f6b26fSZixu Wang class ExtractAPIConsumer : public ASTConsumer {
18589f6b26fSZixu Wang public:
186*5ef2ec7eSDaniel Grumberg   ExtractAPIConsumer(ASTContext &Context, StringRef ProductName,
187*5ef2ec7eSDaniel Grumberg                      std::unique_ptr<raw_pwrite_stream> OS)
188*5ef2ec7eSDaniel Grumberg       : Visitor(Context), ProductName(ProductName), OS(std::move(OS)) {}
18989f6b26fSZixu Wang 
19089f6b26fSZixu Wang   void HandleTranslationUnit(ASTContext &Context) override {
19189f6b26fSZixu Wang     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
19289f6b26fSZixu Wang     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
19389f6b26fSZixu Wang 
19489f6b26fSZixu Wang     // Setup a SymbolGraphSerializer to write out collected API information in
19589f6b26fSZixu Wang     // the Symbol Graph format.
19689f6b26fSZixu Wang     // FIXME: Make the kind of APISerializer configurable.
197*5ef2ec7eSDaniel Grumberg     SymbolGraphSerializer SGSerializer(Visitor.getAPI(), ProductName);
19889f6b26fSZixu Wang     SGSerializer.serialize(*OS);
19989f6b26fSZixu Wang   }
20089f6b26fSZixu Wang 
20189f6b26fSZixu Wang private:
20289f6b26fSZixu Wang   ExtractAPIVisitor Visitor;
203*5ef2ec7eSDaniel Grumberg   std::string ProductName;
20489f6b26fSZixu Wang   std::unique_ptr<raw_pwrite_stream> OS;
20589f6b26fSZixu Wang };
20689f6b26fSZixu Wang 
20789f6b26fSZixu Wang } // namespace
20889f6b26fSZixu Wang 
20989f6b26fSZixu Wang std::unique_ptr<ASTConsumer>
21089f6b26fSZixu Wang ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
21189f6b26fSZixu Wang   std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile);
21289f6b26fSZixu Wang   if (!OS)
21389f6b26fSZixu Wang     return nullptr;
214*5ef2ec7eSDaniel Grumberg   return std::make_unique<ExtractAPIConsumer>(
215*5ef2ec7eSDaniel Grumberg       CI.getASTContext(), CI.getInvocation().getFrontendOpts().ProductName,
21689f6b26fSZixu Wang       std::move(OS));
21789f6b26fSZixu Wang }
21889f6b26fSZixu Wang 
21989f6b26fSZixu Wang std::unique_ptr<raw_pwrite_stream>
22089f6b26fSZixu Wang ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
22189f6b26fSZixu Wang   std::unique_ptr<raw_pwrite_stream> OS =
22289f6b26fSZixu Wang       CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
22389f6b26fSZixu Wang                                  /*RemoveFileOnSignal=*/false);
22489f6b26fSZixu Wang   if (!OS)
22589f6b26fSZixu Wang     return nullptr;
22689f6b26fSZixu Wang   return OS;
22789f6b26fSZixu Wang }
228