186 lines · cpp
1//===- CXExtractAPI.cpp - libclang APIs for manipulating CXAPISet ---------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines all libclang APIs related to manipulation CXAPISet10//11//===----------------------------------------------------------------------===//12 13#include "CXCursor.h"14#include "CXString.h"15#include "CXTranslationUnit.h"16#include "clang-c/CXErrorCode.h"17#include "clang-c/Documentation.h"18#include "clang-c/Index.h"19#include "clang-c/Platform.h"20#include "clang/AST/Decl.h"21#include "clang/AST/DeclBase.h"22#include "clang/AST/DeclObjC.h"23#include "clang/Basic/TargetInfo.h"24#include "clang/ExtractAPI/API.h"25#include "clang/ExtractAPI/ExtractAPIVisitor.h"26#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"27#include "clang/Frontend/ASTUnit.h"28#include "clang/Frontend/FrontendOptions.h"29#include "clang/Index/USRGeneration.h"30#include "llvm/ADT/SmallString.h"31#include "llvm/Support/CBindingWrapping.h"32#include "llvm/Support/Casting.h"33#include "llvm/Support/JSON.h"34#include "llvm/Support/raw_ostream.h"35 36using namespace clang;37using namespace clang::extractapi;38 39namespace {40struct LibClangExtractAPIVisitor41 : ExtractAPIVisitor<LibClangExtractAPIVisitor> {42 using Base = ExtractAPIVisitor<LibClangExtractAPIVisitor>;43 44 LibClangExtractAPIVisitor(ASTContext &Context, APISet &API)45 : ExtractAPIVisitor<LibClangExtractAPIVisitor>(Context, API) {}46 47 const RawComment *fetchRawCommentForDecl(const Decl *D) const {48 if (const auto *Comment = Base::fetchRawCommentForDecl(D))49 return Comment;50 51 return Context.getRawCommentForAnyRedecl(D);52 }53 54 // We need to visit implementations as well to ensure that when a user clicks55 // on a method defined only within the implementation that we can still56 // provide a symbol graph for it.57 bool VisitObjCImplementationDecl(const ObjCImplementationDecl *Decl) {58 if (!shouldDeclBeIncluded(Decl))59 return true;60 61 auto *Interface = Decl->getClassInterface();62 63 if (!VisitObjCInterfaceDecl(Interface))64 return false;65 66 SmallString<128> USR;67 index::generateUSRForDecl(Interface, USR);68 69 if (auto *InterfaceRecord = dyn_cast_if_present<ObjCInterfaceRecord>(70 API.findRecordForUSR(USR))) {71 recordObjCMethods(InterfaceRecord, Decl->methods());72 recordObjCProperties(InterfaceRecord, Decl->properties());73 recordObjCInstanceVariables(InterfaceRecord, Decl->ivars());74 }75 return true;76 }77};78} // namespace79 80DEFINE_SIMPLE_CONVERSION_FUNCTIONS(APISet, CXAPISet)81 82// Visits the Decl D and it's transitive DeclContexts recursively, starting from83// the outer-most context. This is guaranteed to visit every Decl we need in the84// right order to generate symbol graph information for D.85static void WalkupFromMostDerivedType(LibClangExtractAPIVisitor &Visitor,86 Decl *D) {87 if (auto *Parent = D->getDeclContext())88 WalkupFromMostDerivedType(Visitor, cast<Decl>(Parent));89 90 switch (D->getKind()) {91#define ABSTRACT_DECL(DECL)92#define DECL(CLASS, BASE) \93 case Decl::CLASS: \94 Visitor.WalkUpFrom##CLASS##Decl(static_cast<CLASS##Decl *>(D)); \95 break;96#include "clang/AST/DeclNodes.inc"97 }98}99 100static CXString GenerateCXStringFromSymbolGraphData(llvm::json::Object Obj) {101 llvm::SmallString<0> BackingString;102 llvm::raw_svector_ostream OS(BackingString);103 OS << llvm::formatv("{0}", Value(std::move(Obj)));104 return cxstring::createDup(BackingString.str());105}106 107enum CXErrorCode clang_createAPISet(CXTranslationUnit tu, CXAPISet *out_api) {108 if (cxtu::isNotUsableTU(tu) || !out_api)109 return CXError_InvalidArguments;110 111 ASTUnit *Unit = cxtu::getASTUnit(tu);112 113 auto &Ctx = Unit->getASTContext();114 auto Lang = Unit->getInputKind().getLanguage();115 APISet *API = new APISet(Ctx.getTargetInfo().getTriple(), Lang,116 Unit->getMainFileName().str());117 LibClangExtractAPIVisitor Visitor(Ctx, *API);118 119 for (auto It = Unit->top_level_begin(); It != Unit->top_level_end(); ++It) {120 Visitor.TraverseDecl(*It);121 }122 123 *out_api = wrap(API);124 return CXError_Success;125}126 127void clang_disposeAPISet(CXAPISet api) { delete unwrap(api); }128 129CXString clang_getSymbolGraphForUSR(const char *usr, CXAPISet api) {130 auto *API = unwrap(api);131 132 if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(usr, *API))133 return GenerateCXStringFromSymbolGraphData(std::move(*SGF));134 135 return cxstring::createNull();136}137 138CXString clang_getSymbolGraphForCursor(CXCursor cursor) {139 cursor = clang_getCursorReferenced(cursor);140 CXCursorKind Kind = clang_getCursorKind(cursor);141 if (!clang_isDeclaration(Kind))142 return cxstring::createNull();143 144 const Decl *D = cxcursor::getCursorDecl(cursor);145 146 if (!D)147 return cxstring::createNull();148 149 CXTranslationUnit TU = cxcursor::getCursorTU(cursor);150 if (!TU)151 return cxstring::createNull();152 153 ASTUnit *Unit = cxtu::getASTUnit(TU);154 155 auto &Ctx = Unit->getASTContext();156 157 auto Lang = Unit->getInputKind().getLanguage();158 APISet API(Ctx.getTargetInfo().getTriple(), Lang,159 Unit->getMainFileName().str());160 LibClangExtractAPIVisitor Visitor(Ctx, API);161 162 const Decl *CanonicalDecl = D->getCanonicalDecl();163 CanonicalDecl = CanonicalDecl ? CanonicalDecl : D;164 165 SmallString<128> USR;166 if (index::generateUSRForDecl(CanonicalDecl, USR))167 return cxstring::createNull();168 169 WalkupFromMostDerivedType(Visitor, const_cast<Decl *>(CanonicalDecl));170 auto *Record = API.findRecordForUSR(USR);171 172 if (!Record)173 return cxstring::createNull();174 175 for (const auto &Fragment : Record->Declaration.getFragments()) {176 if (Fragment.Declaration)177 WalkupFromMostDerivedType(Visitor,178 const_cast<Decl *>(Fragment.Declaration));179 }180 181 if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(USR, API))182 return GenerateCXStringFromSymbolGraphData(std::move(*SGF));183 184 return cxstring::createNull();185}186